我想知道改变某些按钮行为的“最佳做法”,以执行以下操作:
我想点击一下即可显示一个菜单。或者当您拖动同一个按钮时,您可以将其拖放到另一个按钮中,这将“绘制”连接它们的线。
这是一个例子: 我们的想法是将这些“插孔”按钮连接到任何其他“输入”按钮。
我正在使用Qt设计器,我意识到按钮属性仅列出了“acceptDrops”属性,但我无法使其工作。 信号/插槽没有列出有关拖放的内容。
所以我认为唯一的方法是通过代码创建“自定义小部件”或“重新实现”按钮。可能与信号/插槽相同的东西
如果我不想修改pyuic生成的文件,最好的方法是什么?
更新:我尝试的方法是使用Qt设计器和“推荐的小部件”选项。这允许我创建单独的类文件并重新实现一些元素。我已经通过将PushButton提升为“DragButton”并为其创建了一个类来进行测试:
从PyQt4导入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()
我得到了一些提示并从这篇文章中摘取了片段: PyQt4 - Drag and Drop
此时我可以拖动此按钮,并将其放入另一个在Qt设计器中将“acceptDrops”属性设置为true的相同类型。 但是,我仍然想限制某些按钮的拖动(可能是通过使用UpdateUi方法在主文件中设置),因为有些按钮仅用于接受放置
更新2:现在我正在尝试编写一个绘制线条或连接这些按钮的“连线”的类。
我正在尝试在两个小部件(两个按钮)之间绘制一条线,将它们的位置作为参考。但是当我尝试时,线条被绘制在错误的位置。我也尝试使用mapToGlobal或mapToParent等函数,但结果不同,但仍然错误。 在同一个类中,我有另一种用鼠标绘制线条的方法,并且工作正常。我把它当作参考或示例,但似乎事件位置具有不同的坐标系。好吧,我不知道为什么会发生这种情况。
按钮和图形视图位于窗口小部件内,窗口小部件也在窗口内。
这是课堂,我们谈论的方法是 来自PyQt4导入QtGui,QtCore
class WiringGraphicsView(QtGui.QGraphicsView):
def __init__(self, parent):
QtGui.QGraphicsView.__init__(self, parent)
self.setScene(QtGui.QGraphicsScene(self))
self.setSceneRect(QtCore.QRectF(self.viewport().rect()))
def mousePressEvent(self, event):
self._start = event.pos()
def mouseReleaseEvent(self, event):
start = QtCore.QPointF(self.mapToScene(self._start))
end = QtCore.QPointF(self.mapToScene(event.pos()))
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0) )
pen = QtGui.QPen(brush, 2)
line = QtGui.QGraphicsLineItem(QtCore.QLineF(start, end))
line.setPen(pen)
self.scene().addItem( line )
def paintWire(self, start_widget, end_widget):
start_position = QtCore.QPointF(self.mapToScene(start_widget.pos()))
end_position = QtCore.QPointF(self.mapToScene(end_widget.pos()))
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0) )
pen = QtGui.QPen(brush, 2)
line = QtGui.QGraphicsLineItem(QtCore.QLineF(start_position, end_position))
line.setPen(pen)
self.scene().addItem( line )
如果有更好的方法来实现这一点,请告诉我。
答案 0 :(得分:2)
为了向使用QtDesigner生成的UI添加代码,您必须使用 pyuic 生成.py文件:
pyuic myform.ui -o ui_myform.py
此ui_myform.py文件包含不应编辑的生成代码,因此稍后您可以使用QtDesigner更改.ui文件,重新运行pyuic,并在不丢失的情况下更新ui_myform.py任何工作。
生成的文件将有一个class Ui_myForm(object)
(以主窗口小部件的名称命名),里面有def setupUi(self, myForm)
方法。可以使用的一种方法是创建自己的class MyForm
(在单独的文件上),它将继承Ui_myForm,以及其他一些Qt类,如QWidget或QDialog:
myform.py:
from ui_myform import Ui_myForm
from PyQt4.QtGui import QDialog
class MyForm(QDialog, Ui_myForm):
def __init__(self, parent = None):
QDialog.__init__(self, parent)
self.setupUi(self) #here Ui_myForm creates all widgets as members
#of this object.
#now you can access every widget defined in
#myForm as attributes of self
#supposing you defined two pushbuttons on your .UI file:
self.pushButtonB.setEnabled(False)
#you can connect signals of the generated widgets
self.pushButtonA.clicked.connect(self.pushButtonAClicked)
def bucar_actualizaciones(self):
self.pushButtonB.setEnabled(True)
小部件的名称是您在QtDesigner上设置的名称,但是很容易检查ui_myform.py以查看可用的小部件和名称。
要在QtDesigner中使用自定义窗口小部件,您可以右键单击该按钮,然后转到提升为... 。你必须输入:
点击添加,然后点击推广。
当你运行pyuic时,它会在ui_myform.py的末尾添加这一行
from mypushbutton import MyPushButton
此外,您将看到生成的代码使用MyPushButton而不是QPushButton
答案 1 :(得分:1)
我的感觉是您可以尝试使用标准QWidget
来实现此目标,但使用QGraphicsScene
/ QGraphicsView
API会更容易。
另请注意,您可以使用QGraphicsProxyWidget
在QWidget
中嵌入QGraphicsScene
。
答案 2 :(得分:0)
如果你想在按钮之间画一条线,这意味着你需要重新实现背景小部件的“paintEvent”(可能是所有子小部件的父小部件),如你所说,这不是最好的做法所以。相反,您需要使用QGraphicsWidget,对于绘图线,您需要使用QGraphicsLineItem。它具有以下成员函数:
setAcceptDrops
dragEnterEvent ( QGraphicsSceneDragDropEvent * )
dragLeaveEvent ( QGraphicsSceneDragDropEvent * )
dragMoveEvent ( QGraphicsSceneDragDropEvent * )
在PyQt4安装文件夹中,应该存在一个命名为examples \ graphicsview \ diagramscene的文件夹,您可以将其作为参考。
答案 3 :(得分:0)
你需要使用QDropEvent,我知道这不是一个很好的答案,只是创建一个QDropEvent函数,并在该函数中检查掉线按钮。
如果firstButton在secondButton上掉落,painter->drawLine(firstButton.pos(), secondButton.pos());
您可以使用其他点来绘制线条。或者您可以使用event->source()
作为拖动按钮。您可能需要使用某些设置定义QPen。当我说其他要点时,我的意思是firstButton.boundingRect().topRight().x(), firstButton.boundingRect.bottomRight().y() - firstButton.boundingRect.height() / 2
请参阅:http://doc.qt.io/qt-5/qdropevent.html
很抱歉,此代码是伪C ++代码,但您可以轻松地将其修改为Python。
示例:
void MainWindow::dropEvent(QDropEvent *event)
{
if(event->pos() == somePoint) //somePoint should be inside target's boundingRect, you need to write basic collision detection
painter->drawLine(event->source().pos(), event->pos()); //You might use other points
}
还有其他拖放事件。您可以更改目标的颜色,例如,如果拖过它。见http://doc.qt.io/qt-5/dnd.html
如果您需要,我可以尝试帮助解决碰撞问题。我不知道是否有更好的方法。可能有。我主要是一个C ++编码器,但我可以提供基本的例子。
下拉菜单。您可以使用带有一些mouseEvents的菜单创建一个简单的QWidget,并使其成为按钮的子项,并设置它的y位置,使其显示在按钮下方。例如(再次,C ++,对不起):
dropDownMenu->setParent(someButton);
dropDownMenu->setPos(someButton.x(), someButton.y() + someButton.boundingRect().height());
您可以使用mouseReleaseEvent隐藏或显示它。只需确保将其隐藏在dropEvent函数中,因此当您拖放它时,它不会显示。
编辑:让我用C ++向您展示一个简单的碰撞检测代码,但很容易适应Python。
if(event->pos() > target.boundingRect().topLeft().x() && event->pos() < target.topRight.x() && event->pos() > target.boundingRect().topRight() && event->pos() < target.boundingRect().bottomRight()) {
//It's on the button.
}
如果您想要更简单的解决方案。只需将您要删除的按钮子类化,然后在其班级中添加拖放事件。这会更容易,也更短。我认为dropEvent也应该在子类中工作。我还没试过。
编辑:如果您仅使用Qt Designer询问如何完成所有这些操作,则无法进行。你需要写一些代码。你不能用Qt Designer开发程序,它只是为了更容易制作ui。您可以在没有Qt Designer的情况下制作软件,但是您无法使用 Qt Designer制作软件。您需要学习一些Python编程和一些PyQt来完成这样的任务。但是Python和Qt都很容易在短时间内掌握它们,Qt文档非常棒。
祝你好运!