更新
This commit is contained in:
@@ -8,7 +8,7 @@ from html import escape
|
||||
from math import ceil
|
||||
from typing import Any
|
||||
|
||||
from PySide6.QtCore import QDateTime, QModelIndex, QRectF, QSize, Qt
|
||||
from PySide6.QtCore import QDateTime, QModelIndex, QRectF, QSize, Qt
|
||||
from PySide6.QtGui import (
|
||||
QColor,
|
||||
QFont,
|
||||
@@ -19,7 +19,8 @@ from PySide6.QtGui import (
|
||||
QTextDocument,
|
||||
QTextOption,
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
from PySide6.QtWidgets import (
|
||||
QAbstractItemView,
|
||||
QApplication,
|
||||
QComboBox,
|
||||
QDateTimeEdit,
|
||||
@@ -563,7 +564,165 @@ def _create_time_cell(value: Any, _row: Any) -> str:
|
||||
return format_record_time(raw)
|
||||
|
||||
|
||||
class DoctorMultiSelect(QWidget):
|
||||
class _DoctorOptionDelegate(QStyledItemDelegate):
|
||||
"""Draw an explicit checked state independent of native item indicators."""
|
||||
|
||||
def sizeHint(self, option: Any, index: QModelIndex) -> QSize:
|
||||
return QSize(220, 36)
|
||||
|
||||
def paint(self, painter: QPainter, option: Any, index: QModelIndex) -> None:
|
||||
checked = index.data(Qt.ItemDataRole.CheckStateRole) == Qt.CheckState.Checked.value
|
||||
painter.save()
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
rect = option.rect.adjusted(2, 1, -2, -1)
|
||||
hovered = bool(option.state & QStyle.StateFlag.State_MouseOver)
|
||||
painter.setPen(Qt.PenStyle.NoPen)
|
||||
painter.setBrush(QColor("#EAF2FF" if checked else "#F3F7FD" if hovered else "#FFFFFF"))
|
||||
painter.drawRoundedRect(rect, 5, 5)
|
||||
box = QRectF(rect.left() + 10, rect.center().y() - 8, 16, 16)
|
||||
painter.setPen(QPen(QColor("#1769E8" if checked else "#B8C7DA"), 1))
|
||||
painter.setBrush(QColor("#1769E8" if checked else "#FFFFFF"))
|
||||
painter.drawRoundedRect(box, 3, 3)
|
||||
if checked:
|
||||
pen = QPen(QColor("#FFFFFF"), 2)
|
||||
pen.setCapStyle(Qt.PenCapStyle.RoundCap)
|
||||
painter.setPen(pen)
|
||||
x, y = int(box.x()), int(box.y())
|
||||
painter.drawLine(x + 4, y + 8, x + 7, y + 11)
|
||||
painter.drawLine(x + 7, y + 11, x + 12, y + 5)
|
||||
font = QFont(body_family())
|
||||
font.setPixelSize(13)
|
||||
painter.setFont(font)
|
||||
painter.setPen(QColor("#273244"))
|
||||
text_rect = rect.adjusted(38, 0, -54, 0)
|
||||
label = painter.fontMetrics().elidedText(str(index.data() or ""), Qt.TextElideMode.ElideRight, text_rect.width())
|
||||
painter.drawText(text_rect, Qt.AlignmentFlag.AlignVCenter, label)
|
||||
if checked:
|
||||
painter.setPen(QColor("#1769E8"))
|
||||
painter.drawText(rect.adjusted(0, 0, -12, 0), Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter, "已选")
|
||||
if option.state & QStyle.StateFlag.State_HasFocus:
|
||||
painter.setBrush(Qt.BrushStyle.NoBrush)
|
||||
painter.setPen(QPen(QColor("#ADC8F2"), 1))
|
||||
painter.drawRoundedRect(rect, 5, 5)
|
||||
painter.restore()
|
||||
|
||||
def editorEvent(self, event: Any, model: Any, option: Any, index: QModelIndex) -> bool:
|
||||
# The list handles whole-row toggles, independent of the native checkbox hit area.
|
||||
return False
|
||||
|
||||
|
||||
class _DoctorOptionsList(QListWidget):
|
||||
@staticmethod
|
||||
def _toggle(item: QListWidgetItem | None) -> None:
|
||||
if item is not None and item.flags() & Qt.ItemFlag.ItemIsEnabled:
|
||||
item.setCheckState(Qt.CheckState.Unchecked if item.checkState() == Qt.CheckState.Checked
|
||||
else Qt.CheckState.Checked)
|
||||
|
||||
def mousePressEvent(self, event: Any) -> None:
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
item = self.itemAt(event.position().toPoint())
|
||||
self.setCurrentItem(item)
|
||||
self._toggle(item)
|
||||
event.accept()
|
||||
return
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event: Any) -> None:
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
event.accept()
|
||||
return
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
def keyPressEvent(self, event: Any) -> None:
|
||||
if event.key() == Qt.Key.Key_Space:
|
||||
if not event.isAutoRepeat():
|
||||
self._toggle(self.currentItem())
|
||||
event.accept()
|
||||
return
|
||||
super().keyPressEvent(event)
|
||||
|
||||
|
||||
class _DoctorSelectionDialog(QDialog):
|
||||
def __init__(self, options: Mapping[int, str], selected: set[int], parent: QWidget) -> None:
|
||||
super().__init__(parent)
|
||||
self.setObjectName("DoctorSelectionDialog")
|
||||
self.setWindowTitle("选择医生")
|
||||
self.resize(420, 500)
|
||||
self.setStyleSheet("""
|
||||
QDialog#DoctorSelectionDialog { background: #FFFFFF; color: #273244; }
|
||||
QListWidget#DoctorOptionsList { background: #FFFFFF; border: 1px solid #DBE5F2; border-radius: 6px; padding: 3px; }
|
||||
QLabel#DoctorSelectionCount { color: #1769E8; font-size: 13px; }
|
||||
QDialog#DoctorSelectionDialog QLineEdit { border: 1px solid #DBE5F2; border-radius: 6px; }
|
||||
QDialog#DoctorSelectionDialog QLineEdit:focus { border-color: #1769E8; }
|
||||
QDialog#DoctorSelectionDialog QPushButton { background: #FFFFFF; color: #273244; border: 1px solid #DBE5F2; border-radius: 6px; }
|
||||
QDialog#DoctorSelectionDialog QPushButton:hover { background: #F3F7FD; border-color: #ADC8F2; }
|
||||
QDialog#DoctorSelectionDialog QPushButton:disabled { color: #8A97A9; }
|
||||
QDialog#DoctorSelectionDialog QPushButton#ConfirmDoctorSelection { background: #1769E8; color: #FFFFFF; border-color: #1769E8; }
|
||||
QDialog#DoctorSelectionDialog QPushButton#ConfirmDoctorSelection:hover { background: #125DCE; border-color: #125DCE; }
|
||||
""")
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(16, 16, 16, 16)
|
||||
layout.setSpacing(10)
|
||||
self.search = QLineEdit(self)
|
||||
self.search.setObjectName("DoctorOptionSearch")
|
||||
self.search.setPlaceholderText("搜索医生姓名")
|
||||
self.search.setClearButtonEnabled(True)
|
||||
layout.addWidget(self.search)
|
||||
summary = QHBoxLayout()
|
||||
self.count_label = QLabel(self)
|
||||
self.count_label.setObjectName("DoctorSelectionCount")
|
||||
summary.addWidget(self.count_label, 1)
|
||||
self.clear_button = QPushButton("清空选择", self)
|
||||
self.clear_button.clicked.connect(self._clear)
|
||||
summary.addWidget(self.clear_button)
|
||||
layout.addLayout(summary)
|
||||
self.listing = _DoctorOptionsList(self)
|
||||
self.listing.setObjectName("DoctorOptionsList")
|
||||
self.listing.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
|
||||
self.listing.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
self.listing.setMouseTracking(True)
|
||||
self.listing.setItemDelegate(_DoctorOptionDelegate(self.listing))
|
||||
for doctor_id, name in sorted(options.items(), key=lambda item: item[1]):
|
||||
item = QListWidgetItem(name)
|
||||
item.setToolTip(name)
|
||||
item.setData(Qt.ItemDataRole.UserRole, doctor_id)
|
||||
item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
|
||||
item.setCheckState(Qt.CheckState.Checked if doctor_id in selected else Qt.CheckState.Unchecked)
|
||||
self.listing.addItem(item)
|
||||
layout.addWidget(self.listing, 1)
|
||||
self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Cancel | QDialogButtonBox.StandardButton.Ok, self)
|
||||
self.buttons.button(QDialogButtonBox.StandardButton.Ok).setText("确定")
|
||||
self.buttons.button(QDialogButtonBox.StandardButton.Ok).setObjectName("ConfirmDoctorSelection")
|
||||
self.buttons.button(QDialogButtonBox.StandardButton.Cancel).setText("取消")
|
||||
self.buttons.accepted.connect(self.accept)
|
||||
self.buttons.rejected.connect(self.reject)
|
||||
layout.addWidget(self.buttons)
|
||||
self.listing.itemChanged.connect(self._update_summary)
|
||||
self.search.textChanged.connect(self._filter)
|
||||
self._update_summary()
|
||||
|
||||
def selected_ids(self) -> set[int]:
|
||||
return {_int(self.listing.item(i).data(Qt.ItemDataRole.UserRole))
|
||||
for i in range(self.listing.count())
|
||||
if self.listing.item(i).checkState() == Qt.CheckState.Checked}
|
||||
|
||||
def _update_summary(self, *_args: Any) -> None:
|
||||
count = len(self.selected_ids())
|
||||
self.count_label.setText(f"已选 {count} 位医生")
|
||||
self.clear_button.setEnabled(count > 0)
|
||||
|
||||
def _filter(self, text: str) -> None:
|
||||
query = text.strip().casefold()
|
||||
for i in range(self.listing.count()):
|
||||
item = self.listing.item(i)
|
||||
item.setHidden(query not in item.text().casefold())
|
||||
|
||||
def _clear(self) -> None:
|
||||
for i in range(self.listing.count()):
|
||||
self.listing.item(i).setCheckState(Qt.CheckState.Unchecked)
|
||||
|
||||
|
||||
class DoctorMultiSelect(QWidget):
|
||||
"""Compact checkable doctor selector fed by list rows/extend data."""
|
||||
|
||||
def __init__(self, parent: QWidget | None = None) -> None:
|
||||
@@ -591,36 +750,14 @@ class DoctorMultiSelect(QWidget):
|
||||
self._options[doctor_id] = name
|
||||
self._update_text()
|
||||
|
||||
def _choose(self) -> None:
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle("选择医生")
|
||||
dialog.resize(360, 430)
|
||||
layout = QVBoxLayout(dialog)
|
||||
listing = QListWidget()
|
||||
for doctor_id, name in sorted(self._options.items(), key=lambda item: item[1]):
|
||||
item = QListWidgetItem(name)
|
||||
item.setData(Qt.ItemDataRole.UserRole, doctor_id)
|
||||
item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
|
||||
item.setCheckState(
|
||||
Qt.CheckState.Checked if doctor_id in self._selected else Qt.CheckState.Unchecked
|
||||
)
|
||||
listing.addItem(item)
|
||||
layout.addWidget(listing)
|
||||
buttons = QDialogButtonBox(
|
||||
QDialogButtonBox.StandardButton.Cancel | QDialogButtonBox.StandardButton.Ok
|
||||
)
|
||||
buttons.accepted.connect(dialog.accept)
|
||||
buttons.rejected.connect(dialog.reject)
|
||||
layout.addWidget(buttons)
|
||||
if dialog.exec() != QDialog.DialogCode.Accepted:
|
||||
return
|
||||
self._selected = {
|
||||
_int(listing.item(index).data(Qt.ItemDataRole.UserRole))
|
||||
for index in range(listing.count())
|
||||
if listing.item(index).checkState() == Qt.CheckState.Checked
|
||||
}
|
||||
self._selected.discard(0)
|
||||
self._update_text()
|
||||
def _choose(self) -> None:
|
||||
dialog = _DoctorSelectionDialog(self._options, self._selected, self)
|
||||
try:
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
self._selected = dialog.selected_ids() - {0}
|
||||
self._update_text()
|
||||
finally:
|
||||
dialog.deleteLater()
|
||||
|
||||
def _update_text(self) -> None:
|
||||
if not self._selected:
|
||||
|
||||
Reference in New Issue
Block a user