在计算机图形用户界面中,拖放是单击虚拟对象并将其拖动到其他位置或另一个虚拟对象的动作(或支持其动作)。通常,它可用于调用多种操作,或在两个抽象对象之间创建各种类型的关联。拖放是图形用户界面的一部分。拖放操作使用户能够直观地执行复杂操作。通常,我们可以拖放两件事:数据或一些图形对象。如果我们将图像从一个应用程序拖到另一个应用程序,我们拖放二进制数据。如果我们在Firefox中拖动一个标签并将其移动到另一个位置,我们就会拖放一个图形组件。
QDrag
QDrag
提供对基于MIME的拖放数据传输的支持。它处理拖放操作的大部分细节。传输的数据包含在QMimeData
对象中。
简单的拖放
在第一个例子中,我们有一个QLineEdit
和一个 QPushButton
。我们从行编辑小部件拖动纯文本并将其拖放到按钮小部件上。按钮的标签将会改变。
该示例提供了简单的拖放操作。
class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
In order to drop text on the QPushButton
widget, we must reimplement some methods. Therefore, we create our own Button
class which will inherit from the QPushButton
class.(为了让拖拽过来的文本落在按钮部件上,我们必须重写某些方法,因为,我们创造了我们自己的Button类,它继承于QPushButton类。)
这个parent就理解为按钮是在哪个窗口上的,这个parent输入的就是那个窗口,默认是没有,None,窗口一般都是QWidget类或者子类。我们这里用的是第二种形式,和button = Button("Button", self)是对应的,str="Button",parent=self。
这里稍微扩充一下QMainWindow的知识,以前我们知道它是继承于QWidget的,并且它有自己的布局。像菜单,状态栏,工具栏等在QWidget类里面没有直接定义的方法,而在QMainWindow里面我们是有的,也正是由于QMainWindow有自己的布局,我们以前曾经试过给QMainWindow使用setLayout会给出提示信息,说这个窗口已经有布局了。
self.setAcceptDrops(True)
We enable drop events for the widget with setAcceptDrops()
.(我们用setAcceptDrops()来启动drop事件。)这个后面会实际尝试一下效果。
def dragEnterEvent(self, e):
if e.mimeData().hasFormat('text/plain'):
e.accept()
else:
e.ignore()
First, we reimplement the dragEnterEvent()
method. We inform about the data type that we accept. In our case it is plain text.(首先,我们重写了dragEnterEvent方法,我们选择我们要接受的数据类型,在上面例子里,我们接受的是纯文本)。
def dropEvent(self, e):
self.setText(e.mimeData().text())
By reimplementing the dropEvent()
method we define what happes at the drop event. Here we change the text of the button widget.(通过重写dropEvent方法,我们重新定义了drop事件里面的内容,这里我们改变按钮部件上显示的文本。)
关于上面两个事件,参考了https://blog.csdn.net/vvsxr/article/details/49384199。
我们先来看一下效果:
当我拖拽文本的时候,按钮出现了一个+号,这个其实是e.accept()的结果,然后松开鼠标按键,文本就出现在了按钮上。
e是QDragEnterEvent的一个实例。关于这个QDragEnterEvent,参考了http://doc.qt.io/qt-5/qdragenterevent.html。
点所有成员的列表。
这里面有accept和ignore,我们点进去看看。
返回要落下的数据和它相关的MIME信息。
看来我们还得学一下QMimeData。
tester是检测函数,getter是得到函数,setter是发送函数。我们上面得例子里,用到了tester和getter。后面得MIME Types是一些数据类型的表示格式。
到此为止和上面例子相关的知识算是学完了,我们来实践一下。
看来默认按钮的self.setAcceptDrops()不是true。不过也可以理解,按钮一般也不会设为true,只是我们例子里有这么个特殊要求。还需要说明的是DragEnterEvent必须重写,虽然我不知道QPushButton默认的是什么,但是经过尝试这个例子里面必须要重写。
我们看到没有设置LineEdit的setAcceptDrops,但是可以直接落下,说明文本框默认的就是true,并且dragEnterEvent,dropEvent默认的设置都是可以直接拖拽过来加入文本内容的。不过这对文本框来说也是正常的设置吧,因为文本框很多时候确实会有这种需求,至少比按钮多。
这个例子结束了总结一下整个过程中的条件。
设置按钮可以在鼠标按键松开时文本:self.setAcceptDrops(True)。
重写按钮的dragEnterEvent可以接受文本:def dragEnterEvent(self, e): if e.mimeData().hasFormat('text/plain'): e.accept() else: e.ignore()
重写按钮的dropEvent,改变按钮的文本: def dropEvent(self, e): self.setText(e.mimeData().text())。还必须要把文本设为可以拖拽的:edit.setDragEnabled(True)。
拖放按钮小部件
在下面的示例中,我们将演示如何拖拽按钮小部件。
把show方法写在类定义外面也没什么,只是执行的时间稍稍延后而已。在我们的代码示例中,我们在窗口中有一个QPushButton。如果我们用鼠标左键单击按钮,则会在控制台上打印“按下”消息。通过右键单击并移动按钮,我们对按钮小部件执行拖拽操作。
dropAction = drag.exec_(Qt.MoveAction)
拖动对象的exec_()
方法开始拖放操作。
找到QPushButton的mouseMoveEvent。
下面的不变指的是const,指的是常值。
先来试一下,看看是什么效果,经过我之前的尝试,已知的可以删掉的语句我都注释掉了,另外我加了三句print以便更好的理解程序。
效果:
其实下面这一行也可以不要,我们就不去看它什么意思了。
dropAction = drag.exec_(Qt.MoveAction)这一行是必须要的,不然按钮移动不了。
这个文档里面没加_是因为文档是c++,c++里面,exec应该不是关键字,但是在python里面是,所以就需要加_。
找了各种文档居然没找到这个Qt.MoveAction。最后还是在https://blog.csdn.net/q408384053/article/details/8171803找到一点信息。
其实这个参数甚至可以不要。来试一下:
上面的gif主要是想说明下面这四句对于拖动按钮来说是必须有的,还有注意看这种拖放方式,只有在鼠标按键松时,按钮位置才会变,也就是说按钮运动的轨迹并不能显示,这其实有点不符合我们的习惯或者说预期。
mimeData = QMimeData()
drag = QDrag(self)
drag.setMimeData(mimeData)
drag.exec_()#类似于app.exec_(),不执行这个,拖放没办法执行;还有show(),没有的话窗口没办法显示。但是其实我可以完全不用QDrag,也能实现,并且更符合我们的习惯,可显示运动轨迹。
效果:
实现按住鼠标右键不松开时按钮跟随鼠标移动,松开时按钮固定位置。
PyQt5绘画系统能够渲染矢量图形,图像和轮廓基于字体的文本。当我们想要更改或增强现有小部件时,或者如果我们从头开始创建自定义小部件时,在应用程序中需要绘画。为了绘图,我们使用PyQt5工具包提供的绘图API。
QPainter
QPainter
在小部件和其他绘图设备上执行低级绘制。它可以绘制从简单线条到复杂形状的所有内容。
paintEvent方法
这幅画是在这个paintEvent()
方法中完成的。绘图代码放在QPainter对象的方法begin()
和 end()
方法之间。它在小部件和其他绘图设备上执行低级绘制。
Drawing Text(绘制文本)
我们首先在窗口的客户区域上绘制一些Unicode文本。
在我们的例子中,我们在Cylliric中绘制了一些文本。文本垂直和水平对齐。
翻译:开始在paint device上绘制,如果成功,返回真,否则返回假,注意所有的绘制设置(setPen,setBrush等)都会在begin被调用的时候重置为默认值。可能会有一些比较严重的错误发生,见下图,注意大多数时候,你可以使用一个constructors而不是begin,这样的话,end在结束的时候会被自动调用。警告:一个paint device一次只能被一个painter来使用绘制图形。
QWidget时QPaintDevice的一个子类。
再来看一下这三句的意思:p.setPen(QColor(168, 34, 3))
qp.setFont(QFont('Decorative', 10))
qp.drawText(event.rect(), Qt.AlignCenter, self.text)
翻译:把painter的颜色设置为给定的颜色。后续的drawText()函数使用此字体。文字颜色与前面的setPen颜色相同。如果你设置了一个不可用的字体,Qt会使用最相近的。font()函数会返回setFont里的字体,fnotinfo()返回真正使用的字体,这两个字体可能一样,但也可能不一样,因为你可能在setFont里给的字体不存在。drawTextw其实有很多格式,例子中用了这种格式。
qp.drawText(event.rect(), Qt.AlignCenter, self.text)的第一个参数event.rect就代表一个矩形框,第二个参数时是上面显示的那些对齐方式,第三个self.text是我们事先准备的文本。
QRec就是创造一个左上角在x,y,宽是wifth,高是height的矩形。
event.rec()就是QWidget窗口不带窗口框架的一个矩形,我们的self.text就要在这个矩形里面绘制出来,因为drawText的参数格式是这么要求的,所以需要这么搞。那么到此为止我们基本上已经看懂了。我们来演示一下:
字体和颜色设置应该放在begin之后,上面其实也有提到,因为begin会把这些重置为默认值。其实end可以不要的,这个上面也有提到。QFont('Decorative', 10)这个10是字体的大小,单位是磅。
这个例子怎么说呢,只是显示个文本文字而已,我们用QLable不就可以实现吗,这样用QPainter还有点麻烦。
画点
点是可以绘制的最简单的图形对象。
在我们的示例中,我们在窗口的客户区域上随机绘制了1000个红点。
最后一句的原文:We draw the point with the drawPoint()
method.
应该译为:我们用drawPoint方法绘制了这一点,draw谷歌翻译错了。
Qt.red参考http://doc.qt.io/qt-5/qt.html#GlobalColor-enum。
size()返回的是一个QSize对象,
x,y都是以窗口左上角为坐标原点。
有了上面例子的解释,这个例子其实不难理解了,里面就是用到了一个随机数而已。
每次扩大窗口size都不一样
颜色
颜色是表示红色,绿色和蓝色(RGB)强度值的组合的对象。有效的RGB值范围为0到255.我们可以通过各种方式定义颜色。最常见的是RGB十进制值或十六进制值。我们还可以使用RGBA值,代表红色,绿色,蓝色和Alpha。这里我们添加一些关于透明度的额外信息 Alpha值255定义完全不透明度,0表示完全透明度,例如颜色不可见。
在我们的例子中,我们绘制了三个彩色矩形。QBrush其实在这里面完全没有用到。
来学习一下例子里面的新东西。
这个虽然是五的文档,但可能也不是最新的。setBrush后面的第二个参数可以是QColor。
qp.setBrush(QColor(200, 0, 0))
qp.drawRect(10, 15, 90, 60)
qp.setBrush(QColor(255, 80, 0, 160))
qp.drawRect(130, 15, 90, 60)
qp.setBrush(QColor(25, 0, 90, 200))
qp.drawRect(250, 15, 90, 60)
有的QColor有三个参数,有的四个,大概是因为最后一个int a是有默认值的,虽然不知道是多少。
x,y是矩阵左上角的坐标。看一下效果:
setPen里面的颜色在这个例子里面是矩形边框的颜色。
QPen
这QPen
是一个基本的图形对象。它用于绘制矩形,椭圆,多边形或其他形状的线条,曲线和轮廓。
在我们的例子中,我们画了六行。线条以六种不同的笔样式绘制。有五种预定义的笔样式。我们还可以创建自定义笔样式。最后一行使用自定义笔样式绘制。
我们自定义的样式是1个破折号,4个空格,5个破折号,4个空格,索引为偶数的是破折号,为奇数的位置是空格,注意列表的第一个元素索引是0,不是1。其实setPen还有这种格式
这个例子用的就是这个。
setDashPattern后面的参数不一定是列表,但必须是iterable类型的,例如元组。
效果:
未完待续。