PyQt4日历应用:单日期选中高亮的信号传递实现问题
解决PyQt日历应用中仅保留最后点击日期蓝色背景的问题
我正在开发一款日历应用,通过排列QGraphicsItemGroup元素实现自定义月视图,所有QGraphicsItemGroup元素存入列表以便遍历。鼠标悬停日期时背景变为灰色,点击日期时背景变为蓝色,但当前所有被点击的日期都会保持蓝色,需求是仅最后点击的日期显示蓝色背景,其余日期元素均为白色背景。我无法实现列表中单个QGraphicsItemGroup元素向列表内所有元素发送信号。
我来帮你搞定这个问题!核心思路是让管理所有日期元素的CalendarList类作为中间协调者,通过自定义信号与槽实现全局状态同步:当某个日期被点击时,通知CalendarList,由它来重置所有其他日期的背景,只保留当前点击的日期为蓝色。
关键修改思路
- 给
DateElement添加信号能力:因为QGraphicsItemGroup不属于QObject体系,需要多继承QObject来支持信号 - 让
CalendarList作为信号接收者:实现槽函数遍历所有元素,重置非当前点击元素的背景 - 绑定信号与槽:每个日期元素创建时,将点击信号连接到
CalendarList的处理函数
修改后的完整代码
# coding=utf-8 from PyQt4.QtCore import Qt, QObject, pyqtSignal import sys from datetime import * from calendar import Calendar from PyQt4.QtGui import (QApplication, QDialog, QGraphicsView, QGraphicsScene, QGraphicsRectItem, QVBoxLayout, QGraphicsSimpleTextItem, QBrush, QFont, QGraphicsItemGroup) class GraficsView(QDialog): def __init__(self, parent=None): monthnames = ("Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "November", "Dezember") super(GraficsView, self).__init__(parent) self.viewbox = QGraphicsView() layout = QVBoxLayout() layout.addWidget(self.viewbox) self.setLayout(layout) self.scene = QGraphicsScene(self.viewbox) self.scene.setSceneRect(0,0,701,501) self.calendarList = CalendarList(datetime(2018,2,1), self.scene) self.viewbox.setScene(self.scene) # 多继承QObject和QGraphicsItemGroup,添加信号支持 class DateElement(QObject, QGraphicsItemGroup): # 定义点击信号,传递当前被点击的日期元素实例 dateClicked = pyqtSignal(object) def __init__(self, scene, status=False, date=datetime.today(), coordinates=()): QObject.__init__(self) QGraphicsItemGroup.__init__(self, scene=scene) wochentage = ("Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag") self.status = status if self.status: self.setdate(date) self.setcoordinates(coordinates) def setdate(self, date): wochentage = ("Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag") self.date = date self.dayname = wochentage[self.date.weekday()] self.daynumber = self.date.day def setcoordinates(self, coordinates): self.coordinates = coordinates self.rectangle = QGraphicsRectItem(self.coordinates[0], self.coordinates[1], self.coordinates[2], self.coordinates[3]) self.rectangle.setAcceptHoverEvents(True) self.fontA = QFont("Verdana", 10, QFont.Bold) self.fontB = QFont("Verdana", 18, QFont.Bold) self.lableday = QGraphicsSimpleTextItem() if self.dayname in ("Samstag", "Sonntag"): self.lableday.setBrush(QBrush(Qt.red)) else: self.lableday.setBrush(QBrush(Qt.black)) self.lableday.setFont(self.fontA) self.lableday.setText(self.dayname) self.lableday.setPos(self.coordinates[0]+3, self.coordinates[1]) self.lablenumber = QGraphicsSimpleTextItem() if self.dayname in ("Samstag", "Sonntag"): self.lablenumber.setBrush(QBrush(Qt.red)) else: self.lablenumber.setBrush(QBrush(Qt.black)) self.lablenumber.setFont(self.fontB) self.lablenumber.setText(str(self.daynumber)+".") self.lablenumber.setPos(self.coordinates[0]+3, self.coordinates[1]+12) self.addToGroup(self.rectangle) self.addToGroup(self.lableday) self.addToGroup(self.lablenumber) def hoverEnterEvent(self, event): self.rectangle.setBrush(QBrush(Qt.lightGray)) self.update() def hoverLeaveEvent(self, event): # 避免覆盖已选中的蓝色背景 if self.rectangle.brush().color() != Qt.blue: self.rectangle.setBrush(QBrush(Qt.white)) self.update() def mousePressEvent(self, event): # 发射信号通知CalendarList处理全局状态 self.dateClicked.emit(self) self.rectangle.setAcceptHoverEvents(False) self.rectangle.setBrush(QBrush(Qt.blue)) self.update() def clearRectangle(self): self.rectangle.setAcceptHoverEvents(True) self.rectangle.setBrush(QBrush(Qt.white)) self.update() # 继承QObject,实现槽函数处理全局状态 class CalendarList(QObject): def __init__(self, date, scene): QObject.__init__(self) self.date = date self.scene = scene self.dateelements = [] self.createdates() def createdates(self): weekcount = 0 daycount = 0 calendarobjekt = Calendar() for dateobjekt in calendarobjekt.itermonthdates(self.date.year, self.date.month): if daycount < 7: if dateobjekt.month == self.date.month: date_elem = DateElement(self.scene, True, dateobjekt, (daycount*100, weekcount*100, 100, 100)) # 绑定每个日期的点击信号到处理函数 date_elem.dateClicked.connect(self.onDateClicked) self.dateelements.append(date_elem) else: self.dateelements.append(DateElement(self.scene)) daycount += 1 else: daycount = 0 weekcount += 1 if dateobjekt.month == self.date.month: date_elem = DateElement(self.scene, True, dateobjekt, (daycount*100, weekcount*100, 100, 100)) date_elem.dateClicked.connect(self.onDateClicked) self.dateelements.append(date_elem) else: self.dateelements.append(DateElement(self.scene)) daycount += 1 def onDateClicked(self, clicked_date): # 遍历所有元素,重置非当前点击的有效日期背景 for elem in self.dateelements: if elem != clicked_date and elem.status: elem.clearRectangle() app = QApplication(sys.argv) form = GraficsView() form.show() app.exec_()
核心修改点说明
DateElement多继承QObject:给原本不支持信号的QGraphicsItemGroup添加信号能力,定义dateClicked信号传递自身实例CalendarList的槽函数:onDateClicked函数负责遍历所有日期元素,调用clearRectangle重置非当前点击元素的背景- 信号绑定:每个有效日期元素创建时,将其点击信号连接到
CalendarList的处理函数,实现全局状态同步 - 优化悬停事件:在
hoverLeaveEvent中增加判断,避免覆盖已选中的蓝色背景
内容的提问来源于stack exchange,提问作者Kingmaker
相关产品推荐
相关产品推荐

