python - QGraphicsItem 在悬停输入事件时发出信号

标签 python pyqt qgraphicsscene qgraphicsitem qgraphicswidget

在输入QGraphicsWidgetQGraphicsItem时发出信号的最佳方法/实践是什么?

在我的MWE中,每当用户将鼠标悬停在QGraphicsScene中的项目上时,我想从Square.hoverEnterEvent触发对MainWindow.update的调用。问题在于 QGraphicsItem/Widget 并不是真正设计用来发出信号的。相反,这些类被设置为处理从 QGraphicsScene 传递给它们的事件。 QGraphicsScene 处理用户选择了一个项目但似乎不处理鼠标输入事件的情况,至少没有让 entryEvent 渗透到父窗口小部件的机制/窗口。

import sys
from PyQt5.QtWidgets import QWidget, QApplication, qApp, QMainWindow, QGraphicsScene, QGraphicsView, QStatusBar, QGraphicsWidget, QStyle
from PyQt5.QtCore    import Qt, QSizeF

class Square(QGraphicsWidget) :
 """
 doc string
 """
 def __init__(self,*args, name = None, **kvps) :
  super().__init__(*args, **kvps)
  self.radius = 5
  self.name = name
  self.setAcceptHoverEvents(True)

 def sizeHint(self, hint, size):
  size = super().sizeHint(hint, size)
  print(size)
  return QSizeF(50,50)

 def paint(self, painter, options, widget):
  self.initStyleOption(options)
  ink = options.palette.highLight() if options.state == QStyle.State_Selected else options.palette.button()
  painter.setBrush(ink) # ink
  painter.drawRoundedRect(self.rect(), self.radius, self.radius)

 def hoverEnterEvent(self, event) :
  print("Enter Event")
  super().hoverEnterEvent(event)

class MainWindow(QMainWindow):

 def __init__(self, *args, **kvps) : 
  super().__init__(*args, **kvps)
  # Status bar
  self.stat = QStatusBar(self)
  self.setStatusBar(self.stat)
  self.stat.showMessage("Started")
  # Widget(s)
  self.data = QGraphicsScene(self)
  self.view = QGraphicsView(self.data, self)
  item = self.data.addItem(Square())
  self.view.ensureVisible(self.data.sceneRect())
  self.setCentralWidget(self.view)
  # Visibility
  self.showMaximized()

 def update(self, widget) : 
  self.stat.showMessage(str(widget.name))

if __name__ == "__main__" :
 # Application
 app = QApplication(sys.argv)
 # Scene Tests
 main = MainWindow()
 main.show()
 # Loop
 sys.exit(app.exec_())

文档指出,QGraphicsItem 并非旨在发出信号,而是旨在响应 QGraphicsScene 发送给它的事件。相比之下,QGraphicsWidget 似乎就是为了这样做而设计的,但我不完全确定入口点应该在哪里。就我个人而言,我认为 QGraphicsScene 确实应该发出这些信号,根据我对设计的理解,但我也不确定在这种情况下入口点应该在哪里。

目前我看到以下可能的解决方案,其中#3 是首选方法。我想知道其他人是否有更好的策略:

  1. 为每个 QGraphicsItem/QGraphicsWidget 创建一个 QGraphicsScene 子类,我们将其称为 Scene,并在 Scene 上调用自定义触发器/信号每个小部件。在这里,我必须对我想要包含在场景中的任何项目进行子类化。
  2. Mainwindow 设置为场景中每个项目或场景本身的事件过滤器,并调用 MainWindow.update
  3. Mainwindow.data 设置为 QGraphicsScene 的子类,我们称之为 Scene,并让它过滤自己的事件,发出 hoverEntry 信号。然后根据需要将 hoverEntry 连接到 MainWindow.update

最佳答案

按照墨菲定律 Ekhumoro已经提供了答案。

似乎应该子类化QGraphicsScene并添加必要的信号。然后从QGraphicsItem/Widget触发。这要求对场景中的所有项目进行子类化,以确保它们调用相应的发射函数,但在处理图形场景内容时似乎必须这样做。

如果有人有更好的建议,我不会将此标记为已回答。

import sys
from PyQt5.QtWidgets import QWidget, QApplication, qApp, QMainWindow, QGraphicsScene, QGraphicsView, QStatusBar, QGraphicsWidget, QStyle, QGraphicsItem
from PyQt5.QtCore    import Qt, QSizeF, pyqtSignal

class Square(QGraphicsWidget) :
 """
 doc string
 """
 def __init__(self,*args, name = None, **kvps) :
  super().__init__(*args, **kvps)
  self.radius = 5
  self.name = name
  self.setAcceptHoverEvents(True)
  self.setFlag(self.ItemIsSelectable)
  self.setFlag(self.ItemIsFocusable)

 def sizeHint(self, hint, size):
  size = super().sizeHint(hint, size)
  print(size)
  return QSizeF(50,50)

 def paint(self, painter, options, widget):
  self.initStyleOption(options)
  ink = options.palette.highLight() if options.state == QStyle.State_Selected else options.palette.button()
  painter.setBrush(ink) # ink
  painter.drawRoundedRect(self.rect(), self.radius, self.radius)

 def hoverEnterEvent(self, event) :
  super().hoverEnterEvent(event)
  self.scene().entered.emit(self)
  self.update()

class GraphicsScene(QGraphicsScene) :
 entered = pyqtSignal([QGraphicsItem],[QGraphicsWidget])


class MainWindow(QMainWindow):

 def __init__(self, *args, **kvps) : 
  super().__init__(*args, **kvps)
  # Status bar
  self.stat = QStatusBar(self)
  self.setStatusBar(self.stat)
  self.stat.showMessage("Started")
  # Widget(s)
  self.data = GraphicsScene(self)
  self.data.entered.connect(self.itemInfo)
  self.data.focusItemChanged.connect(self.update)
  self.view = QGraphicsView(self.data, self)
  item = Square(name = "A")
  item.setPos( 50,0)
  self.data.addItem(item)
  item = Square(name = "B")
  item.setPos(-50,0)
  self.data.addItem(item)
  self.view.ensureVisible(self.data.sceneRect())
  self.setCentralWidget(self.view)
  # Visibility
  self.showMaximized()

 def itemInfo(self, item):
  print("Here it is -> ", item)

if __name__ == "__main__" :
 # Application
 app = QApplication(sys.argv)
 # Scene Tests
 main = MainWindow()
 main.show()
 # Loop
 sys.exit(app.exec_())

感兴趣的神奇线条是QGrahicsScene 子类。

class GraphicsScene(QGraphicsScene) :
 entered = pyqtSignal([QGraphicsItem],[QGraphicsWidget])

QGraphicsWidget.hoverEnterEvent 触发entered 信号。 (这就是我陷入困境的地方)

 def hoverEnterEvent(self, event) :
  ...
  self.scene().entered.emit(self)
  ...

以及在MainWindow的init中从self.data = QGraphicsScene(...)self.data = GraphicsScene的切换功能。

关于python - QGraphicsItem 在悬停输入事件时发出信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49492264/

相关文章:

c++ - 如何使 QGraphicsItem 的位置依赖于另一个 QGraphicsItem?

python - 在 jupyter 笔记本上显示 plotly

python - 在 QT 样式表中使用相对 url

python - 如何将给定列表的最大值和最小值转换为元组形式,而不使用 python 内置的 max 和 min 函数

python - 禁用 QDial 鼠标点击和换针

python - pyside qtableview 中的连接事件

c++ - QGraphicsScene.itemAt() 只返回零,整个场景很慢

c++ - 如何通过QGraphicsProxy调整QGraphicScene中添加的QWidget的大小?

Python left() 等效?

python - 在Python中解析HTML数据