Search code examples
pythonpyqtpyqt4qgraphicsviewqgraphicsitem

Signaling from one QGraphicsItem in a list to all items in that list


Background: I am attempting to make a calendar app. I´m creating a custom view of the month by arranging QGraphicsItemGroup elements for each date in the given month.All QGraphicsItemGroup elements are in a list for easy iteration. While the mouse hovers over a date, the background changes to grey. When a date is clicked, the background changes to blue. When another date is clicked it is marked blue and the previous date is cleared.

Month overview

Question: I just want the last clicked date to have a blue background, not all clicked dates. All the other date elements should have a white background. I can´t figure out how to send a signal from one QGraphicsItemGroup element in the date list to all elements in that list.

Current Code:

# coding=utf-8
from PyQt4.QtCore import Qt
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)


class DateElement(QGraphicsItemGroup):

    def __init__(self, scene, status=False, date=datetime.today(), coordinates=()):
        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):
        self.rectangle.setBrush(QBrush(Qt.white))
        self.update()

    def mousePressEvent(self, event):
        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()

class CalendarList():

    def __init__(self, date, scene):
        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:
                    self.dateelements.append(DateElement(self.scene, True, dateobjekt, (daycount*100, weekcount*100, 100, 100)))
                else:
                    self.dateelements.append(DateElement(self.scene))
                daycount += 1
            else:
                daycount = 0
                weekcount += 1
                if dateobjekt.month == self.date.month:
                    self.dateelements.append(DateElement(self.scene, True, dateobjekt, (daycount*100, weekcount*100, 100, 100)))
                else:
                    self.dateelements.append(DateElement(self.scene))
                daycount += 1

app = QApplication(sys.argv)
form = GraficsView()
form.show()
app.exec_()

Possible Solutions:

  1. Create a signal in the CalendarList class, that can be raised by a DateElement, which then triggers the clearBackground methode of all items in the CalendarList.

Solution

  • CalendarList is not a class that inherits from QObject so it does not support the signals, my answer goes in the sense of obtaining the items through scene().items(), then we just filter the items of the DateElement class and have a true status, then the clearRectangle() method will be used:

    def mousePressEvent(self, event):
        for item in self.scene().items():
            if isinstance(item, DateElement):
                if item.status:
                    item.clearRectangle()
        self.rectangle.setAcceptHoverEvents(False)
        self.rectangle.setBrush(QBrush(Qt.blue))
        #self.update()