警告
本部分包含从C++自动翻译到Python的代码片段,可能包含错误。
日历小部件示例#
日历小部件示例展示了如何使用 QCalendarWidget
.
QCalendarWidget
可以显示一个月的日历,并允许用户选择一个日期。日历由四个组件组成:一个允许用户更改显示月份的导航栏、一个每个单元格代表一个月中一天的网格,以及两个显示星期名称和星期数的标题。
日历小部件示例显示了一个 QCalendarWidget
,并允许用户使用 QComboBox
、QCheckBox
和 QDateEdit
配置其外观和行为。此外,用户还可以影响单个日期和标题的格式。
下表总结了 QCalendarWidget
的属性。
属性
描述
当前选择的日期。
可以选择的最早日期。
可以选择的最新日期。
显示为一周第一天的日子(通常是星期日或星期一)。
gridVisible
是否应显示网格。
是否允许用户选择日期。
水平标题中日期名称的格式(例如,“M”、“Mon”或“Monday”)。
垂直标题的格式。
navigationBarVisible
是否显示日历小部件顶部的导航栏。
示例由一个类组成,即 Window
类,该类创建并布局 QCalendarWidget
及其他允许用户配置 QCalendarWidget
的控件。
Window类定义#
以下是对 Window
类的定义
class Window(QWidget): Q_OBJECT # public Window(QWidget parent = None) # private slots def localeChanged(index): def firstDayChanged(index): def selectionModeChanged(index): def horizontalHeaderChanged(index): def verticalHeaderChanged(index): def selectedDateChanged(): def minimumDateChanged(date): def maximumDateChanged(date): def weekdayFormatChanged(): def weekendFormatChanged(): def reformatHeaders(): def reformatCalendarPage(): # private def createPreviewGroupBox(): def createGeneralOptionsGroupBox(): def createDatesGroupBox(): def createTextFormatsGroupBox(): createColorComboBox = QComboBox() previewGroupBox = QGroupBox() previewLayout = QGridLayout() calendar = QCalendarWidget() generalOptionsGroupBox = QGroupBox() localeLabel = QLabel() firstDayLabel = QLabel() ... mayFirstCheckBox = QCheckBox()
通常,表示独立窗口的类大部分API都是私有的。我们将随着实现过程中的遇到,复习这些私有成员。
窗口类实现#
现在,我们来审查类实现,从构造函数开始
def __init__(self, parent): super().__init__(parent) createPreviewGroupBox() createGeneralOptionsGroupBox() createDatesGroupBox() createTextFormatsGroupBox() layout = QGridLayout() layout.addWidget(previewGroupBox, 0, 0) layout.addWidget(generalOptionsGroupBox, 0, 1) layout.addWidget(datesGroupBox, 1, 0) layout.addWidget(textFormatsGroupBox, 1, 1) layout.setSizeConstraint(QLayout.SetFixedSize) setLayout(layout) previewLayout.setRowMinimumHeight(0, calendar.sizeHint().height()) previewLayout.setColumnMinimumWidth(0, calendar.sizeHint().width()) setWindowTitle(tr("Calendar Widget"))
我们首先使用四个私有的 create...GroupBox()
函数创建了四个 QGroupBox
以及它们的子控件(包括 QCalendarWidget
),然后我们将这些分组盒排列在一个 QGridLayout
中。
我们将网格布局的调整大小策略设置为 SetFixedSize
以防止用户调整窗口的大小。在该模式下,窗口的大小将根据其内容控件的尺寸提示自动设置。
为确保在改变 QCalendarWidget
的某个属性时(例如,隐藏导航栏、垂直标题或网格),我们设置第0行的最小高度和第0列的最小宽度为 QCalendarWidget
的初始大小。
接下来,让我们来审查 createPreviewGroupBox()
函数
def createPreviewGroupBox(self): previewGroupBox = QGroupBox(tr("Preview")) calendar = QCalendarWidget() calendar.setMinimumDate(QDate(1900, 1, 1)) calendar.setMaximumDate(QDate(3000, 1, 1)) calendar.setGridVisible(True) calendar.currentPageChanged.connect( self.reformatCalendarPage) previewLayout = QGridLayout() previewLayout.addWidget(calendar, 0, 0, Qt.AlignCenter) previewGroupBox.setLayout(previewLayout)
预览分组盒中只包含一个控件:一个 QCalendarWidget
。我们对其进行设置,将它的 currentPageChanged()
信号连接到我们的 reformatCalendarPage()
信号槽,以确保每个新页面都获得用户指定的格式。
createGeneralOptionsGroupBox()
函数相当大,并且几个控件都是以相同的方式设置的。在这里,我们将查看它的实现的一部分,跳过其余部分
def createGeneralOptionsGroupBox(self): generalOptionsGroupBox = QGroupBox(tr("General Options")) localeCombo = QComboBox() curLocaleIndex = -1 index = 0 for _lang in range(QLocale.C, QLocale.LastLanguage + 1): QLocale.Language lang = QLocale.Language(_lang) locales = QLocale.matchingLocales(lang, QLocale.AnyScript, QLocale.AnyTerritory) for loc in locales: label = QLocale.languageToString(lang) territory = loc.territory() label += '/' label += QLocale.territoryToString(territory) if locale().language() == lang and locale().territory() == territory: curLocaleIndex = index localeCombo.addItem(label, loc) index = index + 1 if curLocaleIndex != -1: localeCombo.setCurrentIndex(curLocaleIndex) localeLabel = QLabel(tr("Locale")) localeLabel.setBuddy(localeCombo) firstDayCombo = QComboBox() firstDayCombo.addItem(tr("Sunday"), Qt.Sunday) firstDayCombo.addItem(tr("Monday"), Qt.Monday) firstDayCombo.addItem(tr("Tuesday"), Qt.Tuesday) firstDayCombo.addItem(tr("Wednesday"), Qt.Wednesday) firstDayCombo.addItem(tr("Thursday"), Qt.Thursday) firstDayCombo.addItem(tr("Friday"), Qt.Friday) firstDayCombo.addItem(tr("Saturday"), Qt.Saturday) firstDayLabel = QLabel(tr("Week starts on:")) firstDayLabel.setBuddy(firstDayCombo) ...
首先,我们从设置“周开始于”组合框开始。此组合框控制哪一天应显示为每周的第一天。
QComboBox 类让我们可以为每个条目附加二维码作为 QVariant 的用户数据。随后可以通过 QComboBox 的 itemData()
函数检索数据。QVariant 不直接支持 Qt::DayOfWeek 数据类型,但它支持 int
,并且 C++ 会将任何枚举值愉快地转换为 int
。
... localeCombo.currentIndexChanged.connect( self.localeChanged) firstDayCombo.currentIndexChanged.connect( self.firstDayChanged) selectionModeCombo.currentIndexChanged.connect( self.selectionModeChanged) gridCheckBox.toggled.connect( calendar.setGridVisible) navigationCheckBox.toggled.connect( calendar.setNavigationBarVisible) horizontalHeaderCombo.currentIndexChanged.connect( self.horizontalHeaderChanged) verticalHeaderCombo.currentIndexChanged.connect( self.verticalHeaderChanged) ...
创建小部件后,我们连接信号和槽。我们将组合框连接到 Window
的私有槽,或者连接到 QComboBox
提供的公共槽。
... firstDayChanged(firstDayCombo.currentIndex()) selectionModeChanged(selectionModeCombo.currentIndex()) horizontalHeaderChanged(horizontalHeaderCombo.currentIndex()) verticalHeaderChanged(verticalHeaderCombo.currentIndex())
函数结束时,我们调用更新日历的槽以确保在启动时 QCalendarWidget 与其他小部件同步。
现在我们来查看 createDatesGroupBox() 私有函数
def createDatesGroupBox(self): datesGroupBox = QGroupBox(tr("Dates")) minimumDateEdit = QDateEdit() minimumDateEdit.setDisplayFormat("MMM d yyyy") minimumDateEdit.setDateRange(calendar.minimumDate(), calendar.maximumDate()) minimumDateEdit.setDate(calendar.minimumDate()) minimumDateLabel = QLabel(tr("Minimum Date:")) minimumDateLabel.setBuddy(minimumDateEdit) currentDateEdit = QDateEdit() currentDateEdit.setDisplayFormat("MMM d yyyy") currentDateEdit.setDate(calendar.selectedDate()) currentDateEdit.setDateRange(calendar.minimumDate(), calendar.maximumDate()) currentDateLabel = QLabel(tr("Current Date:")) currentDateLabel.setBuddy(currentDateEdit) maximumDateEdit = QDateEdit() maximumDateEdit.setDisplayFormat("MMM d yyyy") maximumDateEdit.setDateRange(calendar.minimumDate(), calendar.maximumDate()) maximumDateEdit.setDate(calendar.maximumDate()) maximumDateLabel = QLabel(tr("Maximum Date:")) maximumDateLabel.setBuddy(maximumDateEdit)
在这个函数中,我们创建了最小日期、最大日期和当前日期编辑器小部件,它们控制日历的最小、最大和选定日期。日历的最小和最大日期已经在 createPrivewGroupBox() 中设置,然后我们可以将小部件的默认值设置为日历的值。
currentDateEdit.dateChanged.connect( calendar.setSelectedDate) calendar.selectionChanged.connect( self.selectedDateChanged) minimumDateEdit.dateChanged.connect( self.minimumDateChanged) maximumDateEdit.dateChanged.connect( self.maximumDateChanged) ...
我们将 currentDateEdit 的 dateChanged() 信号直接连接到日历的 setSelectedDate() 槽。当日历的选定日期发生变化时,无论是由于用户操作还是程序操作,我们的 selectedDateChanged() 槽会更新当前日期编辑器。我们还需要对用户更改最小日期和最大日期编辑器做出反应。
下面是 createTextFormatsGroup() 函数
def createTextFormatsGroupBox(self): textFormatsGroupBox = QGroupBox(tr("Text Formats")) weekdayColorCombo = createColorComboBox() weekdayColorCombo.setCurrentIndex( weekdayColorCombo.findText(tr("Black"))) weekdayColorLabel = QLabel(tr("Weekday color:")) weekdayColorLabel.setBuddy(weekdayColorCombo) weekendColorCombo = createColorComboBox() weekendColorCombo.setCurrentIndex( weekendColorCombo.findText(tr("Red"))) weekendColorLabel = QLabel(tr("Weekend color:")) weekendColorLabel.setBuddy(weekendColorCombo)
我们使用 createColorCombo() 设置了工作日颜色和周末颜色组合框,该函数实例化一个 QComboBox 并用颜色(“红色”、“蓝色”等)填充它。
headerTextFormatCombo = QComboBox() headerTextFormatCombo.addItem(tr("Bold")) headerTextFormatCombo.addItem(tr("Italic")) headerTextFormatCombo.addItem(tr("Plain")) headerTextFormatLabel = QLabel(tr("Header text:")) headerTextFormatLabel.setBuddy(headerTextFormatCombo) firstFridayCheckBox = QCheckBox(tr("First Friday in blue")) mayFirstCheckBox = QCheckBox(tr("May 1 in red"))
Header Text Format 组合框允许用户更改用于水平和垂直标题的文本格式(加粗、斜体或普通)。第一个周五蓝色和五一红色复选框会影响特定日期的渲染。
weekdayColorCombo.currentIndexChanged.connect( self.weekdayFormatChanged) weekdayColorCombo.currentIndexChanged.connect( self.reformatCalendarPage) weekendColorCombo.currentIndexChanged.connect( self.weekendFormatChanged) weekendColorCombo.currentIndexChanged.connect( self.reformatCalendarPage) headerTextFormatCombo.currentIndexChanged.connect( self.reformatHeaders) firstFridayCheckBox.toggled.connect( self.reformatCalendarPage) mayFirstCheckBox.toggled.connect( self.reformatCalendarPage)
我们连接了复选框和组合框到各种私有槽。第一个周五蓝色和五一红色复选框都连接到 reformatCalendarPage() 槽,该槽还在日历切换月份时调用。
... reformatHeaders() reformatCalendarPage()
在 createTextFormatsGroupBox() 结束时,我们调用私有槽以使 QCalendarWidget 与其他小部件同步。
我们已经完成了对四个 create...GroupBox()
函数的审查。现在,让我们看看其他的私有函数和槽函数。
QComboBox Window.createColorComboBox() comboBox = QComboBox() comboBox.addItem(tr("Red"), QColor(Qt.red)) comboBox.addItem(tr("Blue"), QColor(Qt.blue)) comboBox.addItem(tr("Black"), QColor(Qt.black)) comboBox.addItem(tr("Magenta"), QColor(Qt.magenta)) return comboBox
在 createColorCombo()
中,我们创建了一个组合框,并用标准颜色填充它。传递给 addItem()
的第二个参数是一个存储用户数据(在这个例子中,是 QColor 对象)的 QVariant 对象。
这个函数被用来设置星期几颜色和周末颜色的组合框。
def firstDayChanged(self, index): calendar.setFirstDayOfWeek(Qt.DayOfWeek( firstDayCombo.itemData(index).toInt()))
当用户更改“周第一天”组合框的值时,将调用 firstDayChanged()
并传回组合框新值的索引。我们使用 itemData()
获取与新当前项关联的自定义数据项,并将其转换为 Qt::DayOfWeek。
selectionModeChanged()
、horizontalHeaderChanged()
和 verticalHeaderChanged()
与 firstDayChanged()
很相似,因此被省略。
def selectedDateChanged(self): currentDateEdit.setDate(calendar.selectedDate())
selectedDateChanged()
更新当前日期编辑器,以反映 QCalendarWidget
的当前状态。
def minimumDateChanged(self, date): calendar.setMinimumDate(date) maximumDateEdit.setDate(calendar.maximumDate())
当用户更改最小日期时,我们通知 QCalenderWidget。我们也更新最大日期编辑器,因为如果新的最小日期晚于当前的最新日期,QCalendarWidget
将自动调整其最大日期,以防止出现矛盾的状态。
def maximumDateChanged(self, date): calendar.setMaximumDate(date) minimumDateEdit.setDate(calendar.minimumDate())
与 minimumDateChanged()
类似的实现方式 maximumDateChanged()
。
def weekdayFormatChanged(self): format = QTextCharFormat() format.setForeground(qvariant_cast<QColor>( weekdayColorCombo.itemData(weekdayColorCombo.currentIndex()))) calendar.setWeekdayTextFormat(Qt.Monday, format) calendar.setWeekdayTextFormat(Qt.Tuesday, format) calendar.setWeekdayTextFormat(Qt.Wednesday, format) calendar.setWeekdayTextFormat(Qt.Thursday, format) calendar.setWeekdayTextFormat(Qt.Friday, format)
每个组合框项都有一个与项文本对应的 QColor 对象作为用户数据。在从组合框中获取颜色后,我们设置每周每一天的文本格式。
日历中列的文本格式以 QTextCharFormat 的形式给出,除了前景色,还允许我们指定各种字符格式信息。在本例中,我们只展示了可能性的一个子集。
def weekendFormatChanged(self): format = QTextCharFormat() format.setForeground(qvariant_cast<QColor>( weekendColorCombo.itemData(weekendColorCombo.currentIndex()))) calendar.setWeekdayTextFormat(Qt.Saturday, format) calendar.setWeekdayTextFormat(Qt.Sunday, format)
weekendFormatChanged()
与 weekdayFormatChanged()
相同,但它影响周六和周日而不是周一至周五。
def reformatHeaders(self): text = headerTextFormatCombo.currentText() format = QTextCharFormat() if text == tr("Bold"): format.setFontWeight(QFont.Bold) elif text == tr("Italic"): format.setFontItalic(True) elif text == tr("Green"): format.setForeground(Qt.green) calendar.setHeaderTextFormat(format)
当用户更改头部文本格式时,将调用 reformatHeaders()
槽函数。我们比较当前头部文本格式组合框的文本,以确定应用哪种格式。(另一种选择是将 QTextCharFormat 值与组合框项一起存储。)
def reformatCalendarPage(self): mayFirstFormat = QTextCharFormat() mayFirst = QDate(calendar.yearShown(), 5, 1) firstFridayFormat = QTextCharFormat() firstFriday = QDate(calendar.yearShown(), calendar.monthShown(), 1) while firstFriday.dayOfWeek() != Qt.Friday: firstFriday = firstFriday.addDays(1) if firstFridayCheckBox.isChecked(): firstFridayFormat.setForeground(Qt.blue) else: // Revert to regular colour for self day of the week. Qt.DayOfWeek dayOfWeek(Qt.DayOfWeek(firstFriday.dayOfWeek())) firstFridayFormat.setForeground(calendar.weekdayTextFormat(dayOfWeek).foreground()) calendar.setDateTextFormat(firstFriday, firstFridayFormat) # When it is checked, "May First in Red" always takes precedence over "First Friday in Blue". if mayFirstCheckBox.isChecked(): mayFirstFormat.setForeground(Qt.red) elif not firstFridayCheckBox.isChecked() or firstFriday not = mayFirst: # We can now be certain we won't be resetting "May First in Red" when we restore # may 1st's regular colour for this day of the week. Qt.DayOfWeek dayOfWeek(Qt.DayOfWeek(mayFirst.dayOfWeek())) calendar.setDateTextFormat(mayFirst, calendar.weekdayTextFormat(dayOfWeek)) calendar.setDateTextFormat(mayFirst, mayFirstFormat)
在 reformatCalendarPage()
中,我们设置月份的第一个周五和当年5月1日的文本格式。实际使用的文本格式取决于哪些复选框被勾选,以及工作日/周末格式是什么。
QCalendarWidget
允许我们使用 setDateTextFormat()
方法设置单个日期的文本格式。我们选择在日历页面更改时设置日期格式 - 即显示新月份时,以及在工作日/周末格式更改时进行设置。我们检查是否有任何 mayFirstCheckBox
和 firstDayCheckBox
被选中,并根据这些设置相应的文本格式。