gpt4 book ai didi

python - 在 QgraphicsView 中拖放不起作用(PyQt)

转载 作者:太空宇宙 更新时间:2023-11-04 01:09:07 24 4
gpt4 key购买 nike

我为一些按钮创建了一个自定义类。这些是“可拖动”按钮,顾名思义,是可以相互拖放的按钮(取决于是否设置了 allowDrag 属性),然后执行操作。这些拖动按钮的代码已经发布在这里: Drag n Drop Button and Drop-down menu PyQt/Qt designer

显然,按钮在 QWidget 中时效果很好,但是当它们被添加到 QGraphicsView 的场景中时(我也为其创建了一个自定义类),放置事件不起作用。我收到了 QGraphicsItem::ungrabMouse: not a mouse grabber 警告。

这是自定义 GraphicsView 的代码:

from PyQt4 import QtGui, QtCore

class WiringGraphicsView(QtGui.QGraphicsView):
#Initializer method
def __init__(self, parent = None, scene=None):
QtGui.QGraphicsView.__init__(self, scene, parent)
#Set Accept Drops property true
self.setAcceptDrops(True)

#This method creates a line between two widgets
def paintWire(self, start_widget, end_widget):
#Size and Position of both widgets
_start = start_widget.geometry()
_end = end_widget.geometry()
#Creates a Brush object with Red color
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0) )
#Creates Pen object with specified brush
pen = QtGui.QPen(brush, 2)
#Create a Line object between two widgets
line = QtGui.QGraphicsLineItem(_start.x() + _start.width() / 2, _start.y() + _start.height() / 2, _end.x() + _end.width() / 2, _end.y() + _end.height() / 2)
#Set the Pen for the Line
line.setPen(pen)
#Add this line item to the scene.
self.scene().addItem( line )

这里是自定义按钮和 graphicsView 所在的代码:

from PyQt4.QtGui import *
from PyQt4.QtCore import *
from dragbutton import DragButton
from wiringgraphicsview import WiringGraphicsView
import icons_rc

app = QApplication([])

scene = QGraphicsScene()

menu = QMenu()

# put a button into the scene and move it
button1 = DragButton('Button 1')
button1.setText("")
button1.setDefault(False)
button1.setAutoDefault(True)
#button1.setMouseTracking(True)
button1.setAllowDrag(True) #Allow Drag n Drop of DragButton
button1.setGeometry(QRect(50, 50, 51, 31)) #Set dimensions of it
#Set icon of button1
icon = QIcon()
icon.addPixmap(QPixmap(":/audio-input-line.png"), QIcon.Normal, QIcon.Off)
button1.setIcon(icon)
button1.setFlat(True)
button1.setMenu(menu)
#Create a QGraphicsProxyWidget adding the widget to scene
scene_button1 = scene.addWidget(button1)
#move the button on the scene
r1 = scene_button1.geometry()
r1.moveTo(-100, -50)

# put another button into the scene
button2 = DragButton('Button 2')
button2.setText("")
#This button shoudn't be dragged, it is just for dropping.
button2.setAllowDrag(False)
button2.setAcceptDrops(True)
icon = QIcon()
icon.addPixmap(QPixmap(":/input_small.png"), QIcon.Normal, QIcon.Off)
button2.setIcon(icon)
#button2.setMouseTracking(True)
#button2.setGeometry(QRect(270, 150, 41, 31))
scene_button2 = scene.addWidget(button2)
scene_button2.setAcceptDrops(True)
r2 = scene_button2.geometry()

# Create the view using the scene
view = WiringGraphicsView(None, scene)
view.resize(300, 200)
view.show()
#and paint a wire between those buttons
view.paintWire(button1, button2)
app.exec_()

另外:如果我想先将按钮嵌入到水平或垂直布局中(让它们按顺序排列),然后再嵌入到 QgraphicsView 中,这可能吗?

编辑:我已经发现您可以将布局及其子按钮添加到图形场景中,就像任何其他小部件一样。我仍然不知道为什么当我在 Qgraphicsscene/QgraphicsView 中时,我在 dragbutton 类中实现的拖放不起作用。我阅读的大部分文档都在讨论如何实现拖放逻辑,但是是在 QgraphicsItem 类中。基于 QGraphicsItem 创建一个新类是个好主意,但此时让我做以下问题:

  • 我应该如何重新实现按钮行为?单击效果、属性和添加 QMenu 的可能性?当我使用 addWidget 将 QButton 或我的自定义 DragButton 添加到场景中时,这已经有效。
  • 布局怎么样?我无法将 QgraphicsItem 添加到布局中,然后将布局添加到场景中!有没有办法让这些项目在场景/ View 中按顺序排列?

编辑 2:我在其他帖子中包含了“DragButton”类的代码,因为它与这个问题相关。

from PyQt4 import QtGui, QtCore

class DragButton(QtGui.QPushButton):

def __init__(self, parent):
super(DragButton, self).__init__(parent)
self.allowDrag = True

def setAllowDrag(self, allowDrag):
if type(allowDrag) == bool:
self.allowDrag = allowDrag
else:
raise TypeError("You have to set a boolean type")

def mouseMoveEvent(self, e):
if e.buttons() != QtCore.Qt.RightButton:
return

if self.allowDrag == True:
# write the relative cursor position to mime data
mimeData = QtCore.QMimeData()
# simple string with 'x,y'
mimeData.setText('%d,%d' % (e.x(), e.y()))
print mimeData.text()

# let's make it fancy. we'll show a "ghost" of the button as we drag
# grab the button to a pixmap
pixmap = QtGui.QPixmap.grabWidget(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.LinkAction | QtCore.Qt.MoveAction) == QtCore.Qt.LinkAction:
print 'linked'
else:
print 'moved'

def mousePressEvent(self, e):
QtGui.QPushButton.mousePressEvent(self, e)
if e.button() == QtCore.Qt.LeftButton:
print 'press'
#AQUI DEBO IMPLEMENTAR EL MENU CONTEXTUAL

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(','))

# move
# so move the dragged button (i.e. event.source())
print e.pos()
#e.source().move(e.pos()-QtCore.QPoint(x, y))
# set the drop action as LinkAction
e.setDropAction(QtCore.Qt.LinkAction)
# tell the QDrag we accepted it
e.accept()

最佳答案

该解决方案似乎要求您将 QGraphicsScene 子类化,以明确地将放置事件传递给放置坐标处的 QGraphicsItem。此外,QGraphicsProxyWidget 似乎没有将放置事件传递给子小部件。同样,您需要子类化 QGraphicsProxyWidget 并手动实例化此类,添加小部件,然后使用 scene.addItem() 手动将实例添加到场景中。

注意:您可能已经意识到,除非您首先与小部件进行交互(例如单击它),否则拖放不会开始。据推测,这也可以通过将 mouseMoveEvent 从场景传递到代理,然后再传递到小部件来解决。

注2:我不知道为什么要花这么大的力气才能完成这项工作。我确实觉得我可能会遗漏一些东西。 documentation说:

QGraphicsProxyWidget supports all core features of QWidget, including tab focus, keyboard input, Drag & Drop, and popups

但如果不进行子类化,我无法让它工作。

相关子类实现:

class MyScene(QGraphicsScene):
def dragEnterEvent(self, e):
e.acceptProposedAction()

def dropEvent(self, e):
# find item at these coordinates
item = self.itemAt(e.scenePos())
if item.setAcceptDrops == True:
# pass on event to item at the coordinates
try:
item.dropEvent(e)
except RuntimeError:
pass #This will supress a Runtime Error generated when dropping into a widget with no MyProxy

def dragMoveEvent(self, e):
e.acceptProposedAction()

class MyProxy(QGraphicsProxyWidget):
def dragEnterEvent(self, e):
e.acceptProposedAction()

def dropEvent(self, e):
# pass drop event to child widget
return self.widget().dropEvent(e)

def dragMoveEvent(self, e):
e.acceptProposedAction()

修改后的应用代码:

scene = MyScene()
...
my_proxy = MyProxy()
my_proxy.setWidget(button2)
my_proxy.setAcceptDrops(True)
scene.addItem(my_proxy)
...

完整的工作(好吧,当拖放成功时它会打印出“已链接”...这是您之前编写的所有内容)应用程序:

from PyQt4 import QtGui, QtCore

class WiringGraphicsView(QtGui.QGraphicsView):
#Initializer method
def __init__(self, parent = None, scene=None):
QtGui.QGraphicsView.__init__(self, scene, parent)
#Set Accept Drops property true
self.setAcceptDrops(True)

#This method creates a line between two widgets
def paintWire(self, start_widget, end_widget):
#Size and Position of both widgets
_start = start_widget.geometry()
_end = end_widget.geometry()
#Creates a Brush object with Red color
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0) )
#Creates Pen object with specified brush
pen = QtGui.QPen(brush, 2)
#Create a Line object between two widgets
line = QtGui.QGraphicsLineItem(_start.x() + _start.width() / 2, _start.y() + _start.height() / 2, _end.x() + _end.width() / 2, _end.y() + _end.height() / 2)
#Set the Pen for the Line
line.setPen(pen)
#Add this line item to the scene.
self.scene().addItem( line )

class DragButton(QtGui.QPushButton):
def __init__(self, parent):
super(DragButton, self).__init__(parent)
self.allowDrag = True

def setAllowDrag(self, allowDrag):
if type(allowDrag) == bool:
self.allowDrag = allowDrag
else:
raise TypeError("You have to set a boolean type")

def mouseMoveEvent(self, e):
if e.buttons() != QtCore.Qt.RightButton:
return QtGui.QPushButton.mouseMoveEvent(self, e)

if self.allowDrag == True:
# write the relative cursor position to mime data
mimeData = QtCore.QMimeData()
# simple string with 'x,y'
mimeData.setText('%d,%d' % (e.x(), e.y()))
# print mimeData.text()

# let's make it fancy. we'll show a "ghost" of the button as we drag
# grab the button to a pixmap
pixmap = QtGui.QPixmap.grabWidget(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.LinkAction | QtCore.Qt.MoveAction) == QtCore.Qt.LinkAction:
print 'linked'
else:
print 'moved'

return QtGui.QPushButton.mouseMoveEvent(self, e)

def mousePressEvent(self, e):

if e.button() == QtCore.Qt.LeftButton:
print 'press'
#AQUI DEBO IMPLEMENTAR EL MENU CONTEXTUAL
return QtGui.QPushButton.mousePressEvent(self, e)

def dragEnterEvent(self, e):
e.accept()
return QtGui.QPushButton.dragEnterEvent(self, e)

def dropEvent(self, e):
# get the relative position from the mime data
mime = e.mimeData().text()
x, y = map(int, mime.split(','))
# move
# so move the dragged button (i.e. event.source())
print e.pos()
# e.source().move(e.pos()-QtCore.QPoint(x, y))
# set the drop action as LinkAction
e.setDropAction(QtCore.Qt.LinkAction)
# tell the QDrag we accepted it
e.accept()

return QtGui.QPushButton.dropEvent(self, QDropEvent(QPoint(e.pos().x(), e.pos().y()), e.possibleActions(), e.mimeData(), e.buttons(), e.modifiers()))



from PyQt4.QtGui import *
from PyQt4.QtCore import *

class MyScene(QGraphicsScene):
def dragEnterEvent(self, e):
e.acceptProposedAction()

def dropEvent(self, e):
# find item at these coordinates
item = self.itemAt(e.scenePos())
if item.setAcceptDrops == True:
# pass on event to item at the coordinates
try:
item.dropEvent(e)
except RuntimeError:
pass #This will supress a Runtime Error generated when dropping into a widget with no ProxyWidget

def dragMoveEvent(self, e):
e.acceptProposedAction()

class MyProxy(QGraphicsProxyWidget):
def dragEnterEvent(self, e):
e.acceptProposedAction()

def dropEvent(self, e):
# pass drop event to child widget
return self.widget().dropEvent(e)

def dragMoveEvent(self, e):
e.acceptProposedAction()


app = QApplication([])

scene = MyScene()

menu = QMenu()

# put a button into the scene and move it
button1 = DragButton('Button 1')
button1.setText("aaa")
button1.setDefault(False)
button1.setAutoDefault(True)
#button1.setMouseTracking(True)
button1.setAllowDrag(True) #Allow Drag n Drop of DragButton
button1.setGeometry(QRect(50, 50, 51, 31)) #Set dimensions of it
#Set icon of button1
icon = QIcon()
icon.addPixmap(QPixmap(":/audio-input-line.png"), QIcon.Normal, QIcon.Off)
button1.setIcon(icon)
button1.setFlat(True)
button1.setMenu(menu)
#Create a QGraphicsProxyWidget adding the widget to scene
scene_button1 = scene.addWidget(button1)
#move the button on the scene
r1 = scene_button1.geometry()
r1.moveTo(-100, -50)

# put another button into the scene
button2 = DragButton('Button 2')
button2.setText("bbb")
#This button shoudn't be dragged, it is just for dropping.
button2.setAllowDrag(False)
button2.setAcceptDrops(True)
icon = QIcon()
icon.addPixmap(QPixmap(":/input_small.png"), QIcon.Normal, QIcon.Off)
button2.setIcon(icon)
#button2.setMouseTracking(True)
#button2.setGeometry(QRect(270, 150, 41, 31))

# Instantiate our own proxy which forwars drag/drop events to the child widget
my_proxy = MyProxy()
my_proxy.setWidget(button2)
my_proxy.setAcceptDrops(True)
scene.addItem(my_proxy)

# Create the view using the scene
view = WiringGraphicsView(None, scene)
view.resize(300, 200)
view.show()
#and paint a wire between those buttons
view.paintWire(button1, button2)
app.exec_()

关于python - 在 QgraphicsView 中拖放不起作用(PyQt),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28780915/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com