QGraphicesItem испускает сигнал при hoverEnterEvent

Каков наилучший метод / практика для подачи сигнала при входе в QGraphicsWidget или QGraphicsItem?

В моем MWE я хотел бы вызвать вызов MainWindow.update, от Square.hoverEnterEventвсякий раз, когда пользователь щелкает мышью по элементу в QGraphicsScene. Беда в том, что 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. Создать QGraphicsScene подкласс, давайте назовем это Scene, к каждому QGraphicsItem/QGraphicsWidget и вызвать пользовательский триггер / сигнал на Scene от каждого виджета. Здесь я должен был бы создать подкласс любого предмета, который я собираюсь включить в сцену.
  2. Задавать Mainwindow в качестве фильтра событий для каждого элемента в сцене или на самой сцене и вызова MainWindow.update,
  3. Задавать Mainwindow.data быть подклассом QGraphicsSceneдавайте назовем это Sceneи пусть он фильтрует свои собственные события, испускающие hoverEntry сигнал. hoverEntry затем подключается к MainWindow.update как необходимо.

1 ответ

Как сказал бы закон Мерфи, Эхуморо уже дает ответ.

Кажется, нужно подкласс QGraphicsScene и добавьте необходимый сигнал. это тогда срабатывает от QGraphicsItem/Widget, Это требует, чтобы все элементы в пределах сцены были разделены на подклассы, чтобы гарантировать, что они вызывают соответствующую функцию emit, но, похоже, они должны это делать в любом случае при работе с графической сценой.

Я не буду отмечать это как ответ на некоторое время в случае, если у кого-то есть лучшее предложение.

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)
  ...

И переключатель из self.data = QGraphicsScene(...) в self.data = GraphicsScene в MainWindowФункция инициализации.

Другие вопросы по тегам