PyQT5 Add a label and icon in a rectangle - python

I've a list of rectangle make in this way:
import sys
from PyQt5.QtCore import Qt, QRect, QRectF
from PyQt5.QtGui import *
from PyQt5.QtWidgets import ( QGridLayout, QWidget, QApplication, QGraphicsScene,
QGraphicsView, QGraphicsProxyWidget, QGraphicsRectItem
)
class WidRect(QWidget):
def __init__(self, mIntMeasureId,mStrLabel=None):
super().__init__()
self.aIntMeasureId=mIntMeasureId
self.aStrLabel=mStrLabel
#self.setWidget(QLabel(mStrLabel))
def mousePressEvent(self, QMouseEvent):
if QMouseEvent.button() == Qt.LeftButton:
print("Left Button Clicked on measure "+ str(self.aIntMeasureId))
elif QMouseEvent.button() == Qt.RightButton:
#do what you want here
print("Right Button Clicked on measure "+ str(self.aIntMeasureId))
class MeasureGrid(QWidget):
#grid = None
#scene = None
def __init__(self, geometry, parent=None):
super(MeasureGrid, self).__init__(parent=parent)
#super().onclick.connec(self.click)
self.aLayMeasureGrid = QGridLayout(self)
self.aGsMeasureScene = QGraphicsScene(self)
self.aGsMeasureScene .setSceneRect(geometry.x(), geometry.y(), geometry.width(), geometry.height())
self.aGsMeasureScene .setBackgroundBrush(Qt.white)
self.aGvMeasureView = QGraphicsView()
self.aGvMeasureView.setScene(self.aGsMeasureScene )
self.aLayMeasureGrid.addWidget(self.aGvMeasureView)
for i in range(1,5):
rect = QRectF(100*i, 20, 80, 140)
self.addRect(rect,i)
def addRect(self, mRecMeasureBox,mIntMeasureNum):
#label = QLabel("World")
#label.setAlignment(Qt.AlignCenter | Qt.AlignCenter)
wrRectBox = WidRect(mIntMeasureNum,"Meas #" + str(mIntMeasureNum))
### CREA IL PROXY ###
gpqProxMeasure = QGraphicsProxyWidget()
gpqProxMeasure.setWidget(wrRectBox)
gpqProxMeasure.setGeometry(mRecMeasureBox)
self.aGsMeasureScene.addItem(gpqProxMeasure)
### CREA L'OGGETTO GENITORE ###
griMeasure = QGraphicsRectItem(mRecMeasureBox)
#rectangle.setFlag(QGraphicsItem.ItemIsMovable, True)
self.aGsMeasureScene.addItem(griMeasure)
gpqProxMeasure.setParentItem(griMeasure)
if __name__ == "__main__":
app = QApplication(sys.argv)
mgMeasureList = MeasureGrid(QRect(10, 10, 550, 280))
mgMeasureList.show()
app.exec_()
I would like to add:
a text into every rectangle
an icon into every rectangle
change the icon with another if I click with right button.
I try to add a QLabel but I cannot add a widget to a widget.
Can you tell me where is my error?
I create the rectangle using a proxy object because in the final version of the application they will be from 100 to 300 objects.

Related

Exact Position of video in QVideoWidget

I have a custom Media Player, that can display images and videos with the help of PyQt. Media player is implemented by the following code in python:
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout,
QLabel, \
QSlider, QStyle, QSizePolicy, QFileDialog
import sys
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtGui import QIcon, QPalette
from PyQt5.QtCore import Qt, QUrl
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("PyQt5 Media Player")
self.setGeometry(350, 100, 700, 500)
self.setWindowIcon(QIcon('player.png'))
p =self.palette()
p.setColor(QPalette.Window, Qt.black)
self.setPalette(p)
self.init_ui()
self.show()
def init_ui(self):
#create media player object
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
#create videowidget object
videowidget = QVideoWidget()
#create open button
openBtn = QPushButton('Open Video')
openBtn.clicked.connect(self.open_file)
#create button for playing
self.playBtn = QPushButton()
self.playBtn.setEnabled(False)
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playBtn.clicked.connect(self.play_video)
#create slider
self.slider = QSlider(Qt.Horizontal)
self.slider.setRange(0,0)
self.slider.sliderMoved.connect(self.set_position)
#create label
self.label = QLabel()
self.label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
#create hbox layout
hboxLayout = QHBoxLayout()
hboxLayout.setContentsMargins(0,0,0,0)
#set widgets to the hbox layout
hboxLayout.addWidget(openBtn)
hboxLayout.addWidget(self.playBtn)
hboxLayout.addWidget(self.slider)
#create vbox layout
vboxLayout = QVBoxLayout()
vboxLayout.addWidget(videowidget)
vboxLayout.addLayout(hboxLayout)
vboxLayout.addWidget(self.label)
self.setLayout(vboxLayout)
self.mediaPlayer.setVideoOutput(videowidget)
#media player signals
self.mediaPlayer.stateChanged.connect(self.mediastate_changed)
self.mediaPlayer.positionChanged.connect(self.position_changed)
self.mediaPlayer.durationChanged.connect(self.duration_changed)
def open_file(self):
filename, _ = QFileDialog.getOpenFileName(self, "Open Video")
if filename != '':
self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))
self.playBtn.setEnabled(True)
def play_video(self):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
def mediastate_changed(self, state):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.playBtn.setIcon(
self.style().standardIcon(QStyle.SP_MediaPause)
)
else:
self.playBtn.setIcon(
self.style().standardIcon(QStyle.SP_MediaPlay)
)
def position_changed(self, position):
self.slider.setValue(position)
def duration_changed(self, duration):
self.slider.setRange(0, duration)
def set_position(self, position):
self.mediaPlayer.setPosition(position)
def handle_errors(self):
self.playBtn.setEnabled(False)
self.label.setText("Error: " + self.mediaPlayer.errorString())
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
What I am trying to do is get the x and y coordinates of the edges of the video/image played each time and while it feels like it should be easy I really can't figure out how to do this. As displayed in the images every video/image may have different corner positions. The only thing I could think of was getting videowidgets dimensions but it wasn't right.
print(videowidget.height())
print(videowidget.width())
print(videowidget.x())
print(videowidget.y())
I'm not sure if this exactly answers your question but I found a sort of solution by comparing aspect ratios of the video and the widget:
class VideoClickWidget(QVideoWidget):
def __init__(self):
QVideoWidget.__init__(self)
def mouseReleaseEvent(self, event):
widget_width = self.frameGeometry().width()
widget_height = self.frameGeometry().height()
widget_ratio = widget_width / widget_height
video_width = self.mediaObject().metaData("Resolution").width()
video_height = self.mediaObject().metaData("Resolution").height()
video_ratio = video_width / video_height
x, y = event.pos().x(), event.pos().y()
# It's wider
if widget_ratio > video_ratio:
percentage = video_ratio / widget_ratio
# we know that video occupies $percentage$ of the widget
dead_zone = int(np.round(widget_width * ((1 - percentage) / 2)))
new_x = np.clip(x - dead_zone, 0, widget_width - 2 * dead_zone)
print(new_x, y)
else:
percentage = widget_ratio / video_ratio
dead_zone = int(np.round(widget_height * ((1 - percentage) / 2)))
new_y = np.clip(y - dead_zone, 0, widget_height - 2 * dead_zone)
print(x, new_y)
super(QVideoWidget, self).mouseReleaseEvent(event)

Aligning popup widget in PyQt5

I've seen a number of replies on SO regarding this matter but not specifically to QMenu and QToolButton. Would appreciate some pointers on aligning the popup widget to the right side of the button. Here's a basic code I'm working off..
import sys
from PyQt5.QtWidgets import *
class test(QWidget):
def __init__(self):
super().__init__()
self.resize(200, 100)
layout = QHBoxLayout(self)
label = QLabel('Testing QToolButton Popup')
toolbutton = QToolButton()
toolbutton.setPopupMode(QToolButton.InstantPopup)
widget = QWidget()
widgetLayout = QHBoxLayout(widget)
widgetLabel = QLabel('Popup Text')
widgetSpinbox = QSpinBox()
widgetLayout.addWidget(widgetLabel)
widgetLayout.addWidget(widgetSpinbox)
widgetAction = QWidgetAction(toolbutton)
widgetAction.setDefaultWidget(widget)
widgetMenu = QMenu(toolbutton)
widgetMenu.addAction(widgetAction)
toolbutton.setMenu(widgetMenu)
layout.addWidget(label)
layout.addWidget(toolbutton)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = test()
win.show()
sys.exit(app.exec_())
The outcome looks like this:
The Qt developer thought the default position was correct, so if you want to modify the alignment you must move the QMenu as I show below:
import sys
from PyQt5.QtCore import QPoint
from PyQt5.QtWidgets import (
QApplication,
QHBoxLayout,
QLabel,
QMenu,
QSpinBox,
QToolButton,
QWidgetAction,
QWidget,
)
class Menu(QMenu):
def showEvent(self, event):
if self.isVisible():
button = self.parentWidget()
if button is not None:
pos = button.mapToGlobal(button.rect().bottomRight())
self.move(pos - self.rect().topRight())
super().showEvent(event)
class Test(QWidget):
def __init__(self):
super().__init__()
self.resize(200, 100)
layout = QHBoxLayout(self)
label = QLabel("Testing QToolButton Popup")
toolbutton = QToolButton(popupMode=QToolButton.InstantPopup)
widgetLabel = QLabel("Popup Text")
widgetSpinbox = QSpinBox()
widget = QWidget()
widgetLayout = QHBoxLayout(widget)
widgetLayout.addWidget(widgetLabel)
widgetLayout.addWidget(widgetSpinbox)
widgetAction = QWidgetAction(toolbutton)
widgetAction.setDefaultWidget(widget)
widgetMenu = Menu(toolbutton)
widgetMenu.addAction(widgetAction)
toolbutton.setMenu(widgetMenu)
layout.addWidget(label)
layout.addWidget(toolbutton)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Test()
win.show()
sys.exit(app.exec_())

PyQt5 - Drag and Drop in QDialog

I have a main pyqt5 application.
On click of a button i would like to call a QDialog and on submit of QDialog want to capture the result of QDialog in main application.
Below is a pyqt module which i like add it to QDialog, is this possible, and want to capture the result as well in main application ?
I don't know where to start.
import sys
from PyQt5.QtCore import QSize
from PyQt5 import QtWidgets, QtCore,QtGui
from PyQt5.QtGui import QColor, QDrag, QPainter, QPixmap
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtWidgets
from win32api import GetSystemMetrics
from PyQt5.QtGui import QImage, QPalette, QBrush
w=GetSystemMetrics(0)/1366
h=GetSystemMetrics(1)/768
class Button(QtWidgets.QPushButton):
def mouseMoveEvent(self, e):
if e.buttons() != QtCore.Qt.LeftButton:
return
# write the relative cursor position to mime data
mimeData = QtCore.QMimeData()
# simple string with 'x,y'
mimeData.setText('%d,%d' % (e.x(), e.y()))
pixmap = QtWidgets.QWidget.grab(self)
# below makes the pixmap half transparent
painter = QtGui.QPainter(pixmap)
painter.setCompositionMode(painter.CompositionMode_DestinationIn)
painter.fillRect(pixmap.rect(), QtGui.QColor(0, 0, 0, 127))
painter.end()
# make a QDrag
drag = QtGui.QDrag(self)
# put our MimeData
drag.setMimeData(mimeData)
# set its Pixmap
drag.setPixmap(pixmap)
# shift the Pixmap so that it coincides with the cursor position
drag.setHotSpot(e.pos())
# start the drag operation
# exec_ will return the accepted action from dropEvent
if drag.exec_(QtCore.Qt.CopyAction | QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction:
print('moved')
else:
print('copied')
class Example(QtWidgets.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
button = Button('Draggable Text', self)
button.setStyleSheet("font-size: 15px;background-color: rgb(255,255,255); margin:5px; border:1px solid rgb(255, 255, 255); ")
button.move(100, 65)
self.buttons = [button]
self.setWindowTitle('Copy or Move')
self.setFixedSize(960, 515)
oImage = QImage(r"C:\Users\vaas\Desktop\background.jpg")
sImage = oImage.scaled(QSize(960*w/1,515*h/1)) # resize Image to widgets size
palette = QPalette()
palette.setBrush(QPalette.ColorRole(10), QBrush(sImage)) # 10 = Windowrole
self.setPalette(palette)
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
# get the relative position from the mime data
mime = e.mimeData().text()
x, y = map(int, mime.split(','))
if e.keyboardModifiers() & QtCore.Qt.ShiftModifier:
# copy
# so create a new button
button = Button('Button', self)
# move it to the position adjusted with the cursor position at drag
button.move(e.pos()-QtCore.QPoint(x, y))
# show it
button.show()
# store it
self.buttons.append(button)
# set the drop action as Copy
e.setDropAction(QtCore.Qt.CopyAction)
else:
# move
# so move the dragged button (i.e. event.source())
print('mine°',e.pos())
e.source().move(e.pos()-QtCore.QPoint(x, y))
# set the drop action as Move
e.setDropAction(QtCore.Qt.MoveAction)
# tell the QDrag we accepted it
e.accept()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Start with http://doc.qt.io/qt-5/dnd.html
Play with the example below:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QSize
from PyQt5.QtGui import (QColor, QDrag, QPainter, QPixmap,
QImage, QPalette, QBrush)
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout
from win32api import GetSystemMetrics
w=GetSystemMetrics(0)/1366
h=GetSystemMetrics(1)/768
### +++++++++++++++++++++++++++++
class Window2(QtWidgets.QWidget):
def __init__(self):
super().__init__()
# To be able to receive media dropped on a widget, call setAcceptDrops(true)
# for the widget, and reimplement the dragEnterEvent() and
# dropEvent() event handler functions.
self.setAcceptDrops(True)
self.setGeometry(QtCore.QRect(20, 50, 180, 600))
self.lay = QVBoxLayout()
self.lay.addWidget(QtWidgets.QPushButton('Button1'))
self.lay.addWidget(QtWidgets.QPushButton('Button2'))
self.lay.addWidget(QtWidgets.QPushButton('Button3'))
palette = self.palette()
palette.setColor(QPalette.Window, QColor('blue'))
self.setPalette(palette)
self.setLayout(self.lay)
self.textLabel = ''
# The dragEnterEvent() function is typically used to inform Qt about the types
# of data that the widget accepts. You must reimplement this function
# if you want to receive either QDragMoveEvent or QDropEvent
# in your reimplementations of dragMoveEvent() and dropEvent().
def dragEnterEvent(self, e):
e.accept()
# The dropEvent() is used to unpack dropped data and
# handle it in way that is suitable for your application.
def dropEvent(self, e):
#self.textLabel = 'Drag and Drop. QLabel. e.mimeData().text() -> `{}`'.format(e.mimeData().text())
self.lay.addWidget(QtWidgets.QLabel('Drag and Drop. QLabel. e.mimeData().text() -> `{}`'\
.format(e.mimeData().text())))
e.accept()
class Button(QtWidgets.QPushButton):
def mouseMoveEvent(self, e):
if e.buttons() != QtCore.Qt.LeftButton:
return
# write the relative cursor position to mime data
mimeData = QtCore.QMimeData()
# simple string with 'x,y'
mimeData.setText('%d,%d' % (e.x(), e.y()))
pixmap = QtWidgets.QWidget.grab(self)
# below makes the pixmap half transparent
painter = QtGui.QPainter(pixmap)
painter.setCompositionMode(painter.CompositionMode_DestinationIn)
painter.fillRect(pixmap.rect(), QtGui.QColor(0, 0, 0, 127))
painter.end()
# make a QDrag
drag = QtGui.QDrag(self)
# put our MimeData
drag.setMimeData(mimeData)
# set its Pixmap
drag.setPixmap(pixmap)
# shift the Pixmap so that it coincides with the cursor position
drag.setHotSpot(e.pos())
# start the drag operation
# exec_ will return the accepted action from dropEvent
if drag.exec_(QtCore.Qt.CopyAction | QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction:
print('moved')
else:
print('copied')
class Example(QtWidgets.QWidget):
def __init__(self):
super(Example, self).__init__()
self.w2 = Window2() # +++
self.w2.show() # +++
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
button = Button('Draggable Text', self)
button.setStyleSheet("font-size: 15px;background-color: rgb(255,255,255); margin:5px; border:1px solid rgb(255, 255, 255); ")
button.move(100, 65)
self.buttons = [button]
self.setWindowTitle('Copy or Move')
self.setFixedSize(960, 515)
oImage = QImage(r"C:\Users\vaas\Desktop\background.jpg")
#oImage = QImage(r"logo.png")
sImage = oImage.scaled(QSize(960*w/1,515*h/1)) # resize Image to widgets size
palette = QPalette()
palette.setBrush(QPalette.ColorRole(10), QBrush(sImage)) # 10 = Windowrole
self.setPalette(palette)
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
# get the relative position from the mime data
mime = e.mimeData().text()
x, y = map(int, mime.split(','))
if e.keyboardModifiers() & QtCore.Qt.ShiftModifier:
# copy
# so create a new button
button = Button('Button', self)
# move it to the position adjusted with the cursor position at drag
button.move(e.pos()-QtCore.QPoint(x, y))
# show it
button.show()
# store it
self.buttons.append(button)
# set the drop action as Copy
e.setDropAction(QtCore.Qt.CopyAction)
else:
# move
# so move the dragged button (i.e. event.source())
print('mine°',e.pos())
e.source().move(e.pos()-QtCore.QPoint(x, y))
# set the drop action as Move
e.setDropAction(QtCore.Qt.MoveAction)
# tell the QDrag we accepted it
e.accept()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

How to add a pixmap drawn in QGraphicsItem to QGraphicsScene? [duplicate]

This question already exists:
How to make the picture zoom (QGraphicsItem), and make scrollbars change their size accordingly (QGraphicsScene)? [duplicate]
Closed 4 years ago.
How to add a pixmap drawn in QGraphicsItem to QGraphicsScene in the following example?
#!/usr/bin/env python
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QGraphicsItem, QGraphicsView, QGraphicsScene, QMainWindow
class TicTacToe(QGraphicsItem):
def __init__(self, helper):
super(TicTacToe, self).__init__()
self.mypixmap = QPixmap("exit.png")
def paint(self, painter, option, widget):
painter.setOpacity(1)
painter.drawPixmap(0,0, 300, 300, self.mypixmap)
def boundingRect(self):
return QRectF(0,0,300,300)
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
scene = QGraphicsScene(self)
self.tic_tac_toe = TicTacToe(self)
scene.addItem(self.tic_tac_toe)
self.setScene(scene)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.y = MyGraphicsView()
self.setCentralWidget(self.y)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Example()
w.show()
sys.exit(app.exec_())
Try it:
Drag and drop image,
Enlarge Image -> Qt.Key_Right,
Reduce image <- Qt.Key_Left.
from PyQt5.QtCore import (QLineF, QPointF, QRectF,
pyqtSignal, QStandardPaths, Qt)
from PyQt5.QtGui import (QIcon, QBrush, QColor, QPainter, QPixmap)
from PyQt5.QtWidgets import (QAction, QMainWindow, QApplication,
QGraphicsObject,
QGraphicsView, QGraphicsScene, QGraphicsItem,
QGridLayout, QVBoxLayout, QHBoxLayout,
QFileDialog, QLabel, QLineEdit, QPushButton)
from PyQt5.QtOpenGL import QGLFormat
class MyGraphicsView(QGraphicsView):
backgroundColor = QColor(28, 31, 34) # Background color
def __init__(self):
super(MyGraphicsView, self).__init__()
self.resize(800, 600)
self.setBackgroundBrush(self.backgroundColor)
self.setCacheMode(self.CacheBackground)
self.setRenderHints(
QPainter.Antialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform)
if QGLFormat.hasOpenGL():
self.setRenderHint(QPainter.HighQualityAntialiasing)
self.setViewportUpdateMode(self.SmartViewportUpdate)
self._scene = QGraphicsScene(-400, -300, 800, 600, self)
self.setScene(self._scene)
self._itemImage = None
def keyReleaseEvent(self, event):
""" Button processing event """
self._scaleImage(event)
super(MyGraphicsView, self).keyReleaseEvent(event)
def closeEvent(self, event):
""" Clear all items in the scene when the window is `closed` """
self._scene.clear()
self._itemImage = None
super(MyGraphicsView, self).closeEvent(event)
def _scaleImage(self, event):
""" Image zoom operation """
if not self._itemImage:
return
scale = self._itemImage.scale()
if event.key() == Qt.Key_Right:
# Enlarge Image -> Qt.Key_Right
if scale >= 0.91:
return
self._itemImage.setScale(scale + 0.1)
elif event.key() == Qt.Key_Left:
# Reduce image <- Qt.Key_Left
if scale <= 0.11:
return
self._itemImage.setScale(scale - 0.1)
def loadImage(self):
path, _ = QFileDialog.getOpenFileName(
self, 'Please select an image',
QStandardPaths.writableLocation(QStandardPaths.DesktopLocation),
'Image(*.jpg *.png)')
if not path:
return
if self._itemImage:
# Delete previous item
self._scene.removeItem(self._itemImage)
del self._itemImage
self._itemImage = self._scene.addPixmap(QPixmap(path))
self._itemImage.setFlag(QGraphicsItem.ItemIsMovable)
self._itemImage.setScale(0.1) # Default load factor
size = self._itemImage.pixmap().size()
# Adjust the image in the middle
self._itemImage.setPos(
-size.width() * self._itemImage.scale() / 2,
-size.height() * self._itemImage.scale() / 2
)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = MyGraphicsView()
w.show()
ww = QPushButton('Select a file', clicked=w.loadImage)
ww.show()
sys.exit(app.exec_())

Let children of QGraphicsItemGroup handle their own event

Right now, I am learning QGraphicsView. I followed a tutorial given by Nokia from 2011 (YouTube: Qt DevDays 2011, Advanced Qt -A Deep Dive 1/6). It is a very basic demo showing a house (made from QRect and QPolygon) that turns around if clicked. On the backside of the house are a few widgets (e.g. QCombobox) aranged inside a QGraphicsProxyWidget hold by a QGraphicsItemGroup. Now I am facing the problem that since Qt 4.7 the class QGraphicItemsGroup no longer supports the flag "setHandlesChildEvents(False)".
Since QGraphicsItemGroup will handle all the events, how can I achieve that the QGraphicsItem Group will not block the child item's event, as well as letting child item handle it own event?
Thanks in advance for your help!
main_house.py
from PyQt5.QtWidgets import QApplication, QGraphicsScene, QGraphicsView
from PyQt5.QtCore import Qt
import sys
from House.house import House
app = QApplication([])
scene = QGraphicsScene()
house_1 = House(Qt.blue, Qt.yellow)
scene.addItem(house_1)
scene.setSceneRect(0, 0, 500, 500)
view = QGraphicsView(scene)
view.show()
view.resize(600, 600)
sys.exit(app.exec_())
house.py
from PyQt5.QtWidgets import QGraphicsItemGroup,QGraphicsProxyWidget,QGraphicsRectItem,QGraphicsPolygonItem, \
QWidget, QVBoxLayout, QLabel, QComboBox, QPushButton
from PyQt5.QtCore import QTimeLine, QPointF, QRectF, Qt
from PyQt5.QtGui import QColor, QPolygonF, QTransform
class House(QGraphicsItemGroup):
def __init__(self, roofcolor: QColor, facadecolor: QColor, parent=None):
super(House, self).__init__(parent)
self._front = True
self._timeline = None
self._graphics = None
self._configpage = None
self._facade = None
self._roof = None
self.roof_polygon = QPolygonF()
self.roof_polygon << QPointF(0, 200) << QPointF(100, 0) << QPointF(200, 200)
self.body_rect = QRectF(0, 200, 200, 200)
self.create_roof(roofcolor)
self.create_front(facadecolor)
self.create_back(roofcolor, facadecolor)
# ==========================
# self.setHandlesChildEvents(False)  # obsolete since Qt 4.7
# ==========================
def mousePressEvent(self, event):
if not self._timeline:
self._timeline = QTimeLine(1000)
self._timeline.setCurveShape(QTimeLine.EaseInOutCurve)
self._timeline.valueChanged.connect(self.rotate_house)
self._timeline.finished.connect(self.reset)
self._timeline.start()
def reset(self):
self._timeline = None
def rotate_house(self, pos):
angle = int(pos * 180)
if self._front is True:
angle += 180
transform = QTransform()
transform.translate(100, 0)
transform.rotate(angle, Qt.YAxis)
transform.translate(-100, 0)
self.setTransform(transform)
if pos == 1.0:
self._front = not self._front
config = angle < 90 or angle >= 270
self._configpage.setVisible(config)
self._facade.setVisible(not config)
def update_roof_color(self):
combo = self.sender()
color = combo.itemData(combo.currentIndex()).value()
self._roof.setBrush(color)
def update_house_color(self):
combo = self.sender()
color = combo.itemData(combo.currentIndex()).value()
self._facade.setBrush(color)
def create_roof(self, color: QColor):
self._roof = QGraphicsPolygonItem(self.roof_polygon)
self.addToGroup(self._roof)
self._roof.setBrush(color)
def create_front(self, color: QColor):
self._facade = QGraphicsRectItem(self.body_rect)
self._facade.setBrush(color)
self.addToGroup(self._facade)
def create_back(self, roof_color: QColor, facade_color: QColor):
self._configpage = QGraphicsProxyWidget()
self._configpage.setWidget(self.create_config_widget(roof_color, facade_color))
self._configpage.setGeometry(self.body_rect)
self.addToGroup(self._configpage)
self._configpage.hide()
# ==== Problem: Widgets (comboboxes) do not receive events ====
def create_config_widget(self, roof_color: QColor, facade_color: QColor) -> QWidget:
res = QWidget()
layout = QVBoxLayout(res)
label = QLabel('Roof color:')
layout.addWidget(label)
self.roof_combo = self.create_color_combobox(roof_color) # combobox does not receive events
layout.addWidget(self.roof_combo)
self.roof_combo.activated.connect(self.update_roof_color)
label = QLabel('House color:')
layout.addWidget(label)
self.facade_combo = self.create_color_combobox(facade_color)
layout.addWidget(self.facade_combo)
self.facade_combo.activated.connect(self.update_house_color)
bt = QPushButton('Test')
bt.setCheckable(True)
layout.addWidget(bt)
layout.addStretch(1)
return res
def create_color_combobox(self, color: QColor) -> QComboBox:
res = QComboBox()
res.addItem('red', QColor(Qt.red))
res.addItem('blue', QColor(Qt.blue))
res.addItem('green', QColor(Qt.green))
res.addItem('white', QColor(Qt.white))
res.setCurrentIndex(res.findData(color))
return res

Categories

Resources