PDA

View Full Version : How to show two states of a rectangle in PyQt/PySide2



illuzioner1
9th March 2020, 02:37
I have what I thought was a simple task. I have an alert button (QGraphicsRectItem) in a graphics scene which has 2 rectangle children, each with its own, same text. I create the alert with rect 1 with a darkGray brush, a white border and white text. Rect 2 is supposed to have a yellow fill brush and black text. When I press a button I hide one set and show the other.

The problem is that the yellow rectangle doesn't show up, but its text does! What am I doing wrong?


import sys
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import QPainter, QColor
#from PyQt5.QtCore import QCoreApplication
from pynput import keyboard


class Geo:
def __init__(self, x, y, w, h):
self.x = x
self.y = y
self.w = w
self.h = h


class Alert(QtWidgets.QGraphicsRectItem):
def __init__(self, key_name, key_char, geo, parent=None, highlight_color=QColor(255, 255, 0, 200)):
super(Alert, self).__init__(parent)
self.key_name = key_name
self.geo = geo

rect = QRectF(0, 0, geo.w, geo.h)
normal = QtWidgets.QGraphicsRectItem(rect, self)
normal.setBrush(Qt.darkGray)
normal.setPen(Qt.white)
normal_txt = QtWidgets.QGraphicsSimpleTextItem(key_char, normal)

brush = QtGui.QBrush(Qt.white)
normal.setBrush(brush)
self.normal = normal

#self.highlighted = QtWidgets.QGraphicsRectItem(normal)
self.highlighted = QtWidgets.QGraphicsRectItem(self)
brush2 = QtGui.QBrush(highlight_color)
self.highlighted.setBrush(brush2)
#self.highlighted.setBrush(highlight_color)
#self.highlighted.setBrush(brush)
txt = QtWidgets.QGraphicsTextItem(key_char, self.highlighted)
txt.setX(geo.w/2)
#self.highlighted.hide()

def highlight(self, on=False):
if on:
self.highlighted.show()
self.normal.hide()
else:
self.highlighted.hide()
self.normal.show()


class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.scene = QtWidgets.QGraphicsScene(self)
self.scene.setItemIndexMethod(QtWidgets.QGraphicsS cene.NoIndex)

geo = Geo(10, 10, 50, 50)
self.buzz = Alert("Buzz", "Buzz", geo)
self.scene.addItem(self.buzz)
self.buzz.setPos(QPointF(130, 120))

self.create_central_widget()

self.listener = keyboard.Listener(
on_press=self.on_press,
on_release=self.on_release)
self.listener.start()

def create_layout(self):
self.layout = QtWidgets.QGraphicsLinearLayout()
self.widget = QtWidgets.QGraphicsWidget()
self.widget.setLayout(self.layout)
self.scene.addItem(self.widget)

width = self.keyboard_width
height = self.keyboard_height
self.setMinimumSize(width, height)
self.scene.setSceneRect(0, 0, width, height)

def create_central_widget(self):
self.view = QtWidgets.QGraphicsView(self.scene)
self.view.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)
self.view.setBackgroundBrush((QColor("bisque")))
self.setCentralWidget(self.view)
self.view.scale(3, 3)

def on_press(self, key):
self.buzz.highlight(True)

def on_release(self, key):
self.buzz.highlight(False)


def main():
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
ret = app.exec_()
sys.exit(ret)

if __name__ == "__main__":
main()

d_stranz
9th March 2020, 20:08
I don't see where you are setting the rect for self.highlighted (which you do set for normal / self.normal). So self.highlighted has zero size.

But this hide / show mechanism seems pretty complicated when all you want to do in actuality is to change the color of a rectangle. Why not simply set a new brush on a -single- QGraphicsRectItem instead of flip / flopping two of them?

illuzioner1
10th March 2020, 02:28
You're right, that was the problem. In my shifting things around I lost the 'rect' in the second rectangle. Good idea on the brush. This is a simplified version of what I'm doing so I'll have to see if that works (as pixmaps are a possibility).

Thank you for the help!

d_stranz
10th March 2020, 04:18
(as pixmaps are a possibility)
Same principle as a brush. In Qt, QPixmap instances are reference-counted, so there is no overhead associated with changing the pixmap for a QGraphicsPixmapItem. QGraphicsPixmapItem::setPixmap() takes a const reference to a QPixmap, which implies that the graphics item stores a local copy, but that's not necessarily the case. Even when setting a pixmap is pass-by-value, the reference counting means that no actual copy is made unless the target of the copy modifies the original pixmap (ie. copy-on-write semantics apply).