我知道对于QComboBox有一种名为editable的方法,您可以使用它手动编辑当前值:
combo.setEditable(True)
。我找不到与QDateEdit类似的东西。我希望用户能够删除整个日期字符串并手动输入年份值或将其留空。
问问题
419 次
1 回答
1
无法进行所请求的编辑,因为 QDateEdit(基于 QDateTimeEdit)继承自 QAbstractSpinBox,QAbstractSpinBox 已经包含 QLineEdit 小部件,但对可以输入的内容有严格的规定。
虽然子类化 QDateEdit是可能的,但它可能有点复杂,因为它使用高级控件(最重要的是“当前部分”,它告诉正在编辑日期的哪一部分)。切换日期格式(“yyyy-MM-dd”和“yyyy”)是可能的,但不是自动的,并且需要大量检查,可能使用正则表达式和高级文本光标控制。
根据我的经验,如果没有错误或意外行为,更改 QDateTimeEdit 类的键盘行为确实很难实现,并且由于此处不需要旋转框的主要功能(向上/向下箭头和向上/向下箭头键),您可以创建嵌入 QLineEdit 和子 QCalendarWidget 的控件,该控件使用按钮作为弹出窗口打开。
我还实现了一个小型验证器来忽略大多数无效输入(但您仍然可以输入无效日期,例如 2020-31-31)。
class SimpleDateValidator(QtGui.QValidator):
def validate(self, text, pos):
if not text:
return self.Acceptable, text, pos
fmt = self.parent().format()
_sep = set(fmt.replace('y', '').replace('M', '').replace('d', ''))
for l in text:
# ensure that the typed text is either a digit or a separator
if not l.isdigit() and l not in _sep:
return self.Invalid, text, pos
years = fmt.count('y')
if len(text) <= years and text.isdigit():
return self.Acceptable, text, pos
if QtCore.QDate.fromString(text, fmt).isValid():
return self.Acceptable, text, pos
return self.Intermediate, text, pos
class DateEdit(QtWidgets.QWidget):
customFormat = 'yyyy-MM-dd'
def __init__(self, parent=None):
super().__init__(parent)
self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
layout = QtWidgets.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.lineEdit = QtWidgets.QLineEdit()
layout.addWidget(self.lineEdit)
self.lineEdit.setMaxLength(len(self.format()))
self.validator = SimpleDateValidator(self)
self.lineEdit.setValidator(self.validator)
self.dropDownButton = QtWidgets.QToolButton()
layout.addWidget(self.dropDownButton)
self.dropDownButton.setIcon(
self.style().standardIcon(QtWidgets.QStyle.SP_ArrowDown))
self.dropDownButton.setMaximumHeight(self.lineEdit.sizeHint().height())
self.dropDownButton.setCheckable(True)
self.dropDownButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.calendar = QtWidgets.QCalendarWidget()
self.calendar.setWindowFlags(QtCore.Qt.Popup)
self.calendar.installEventFilter(self)
self.dropDownButton.pressed.connect(self.showPopup)
self.dropDownButton.released.connect(self.calendar.hide)
self.lineEdit.editingFinished.connect(self.editingFinished)
self.calendar.clicked.connect(self.setDate)
self.calendar.activated.connect(self.setDate)
self.setDate(QtCore.QDate.currentDate())
def editingFinished(self):
# optional: clear the text if the date is not valid when leaving focus;
# this will only work if *NO* validator is set
if self.calendar.isVisible():
return
if not self.isValid():
self.lineEdit.setText('')
def format(self):
return self.customFormat or QtCore.QLocale().dateFormat(QtCore.QLocale.ShortFormat)
def setFormat(self, format):
# only accept numeric date formats
if format and 'MMM' in format or 'ddd' in format:
return
self.customFormat = format
self.setDate(self.calendar.selectedDate())
self.calendar.hide()
self.lineEdit.setMaxLength(self.format())
self.validator.setFormat(self.format())
def text(self):
return self.lineEdit.text()
def date(self):
if not self.isValid():
return None
date = QtCore.QDate.fromString(self.text(), self.format())
if date.isValid():
return date
return int(self.text())
def setDate(self, date):
self.lineEdit.setText(date.toString(self.format()))
self.calendar.setSelectedDate(date)
self.calendar.hide()
def setDateRange(self, minimum, maximum):
self.calendar.setDateRange(minimum, maximum)
def isValid(self):
text = self.text()
if not text:
return False
date = QtCore.QDate.fromString(text, self.format())
if date.isValid():
self.setDate(date)
return True
try:
year = int(text)
start = self.calendar.minimumDate().year()
end = self.calendar.maximumDate().year()
if start <= year <= end:
return True
except:
pass
return False
def hidePopup(self):
self.calendar.hide()
def showPopup(self):
pos = self.lineEdit.mapToGlobal(self.lineEdit.rect().bottomLeft())
pos += QtCore.QPoint(0, 1)
rect = QtCore.QRect(pos, self.calendar.sizeHint())
self.calendar.setGeometry(rect)
self.calendar.show()
self.calendar.setFocus()
def eventFilter(self, source, event):
# press or release the button when the calendar is shown/hidden
if event.type() == QtCore.QEvent.Hide:
self.dropDownButton.setDown(False)
elif event.type() == QtCore.QEvent.Show:
self.dropDownButton.setDown(True)
return super().eventFilter(source, event)
def keyPressEvent(self, event):
if event.key() in (QtCore.Qt.Key_Down, QtCore.Qt.Key_F4):
if not self.calendar.isVisible():
self.showPopup()
super().keyPressEvent(event)
于 2020-09-18T17:18:09.770 回答