Python PyQt6 教程(五)
本文翻译自:ZetCode
Python PyQt6 教程(五)
拖放
在 PyQt6 教程的这一部分,我们将介绍拖放操作。
在计算机图形用户界面中,拖放是单击虚拟对象并将其拖动到不同位置或另一个虚拟对象上的操作(或支持该操作)。通常,它可用于调用多种操作,或在两个抽象对象之间创建各种类型的关联。
拖放是图形用户界面的一部分。拖放操作使用户能够直观地执行复杂操作。
通常,我们可以拖放两样东西:数据或一些图形对象。如果我们将图像从一个应用程序拖到另一个应用程序,我们就会拖放二进制数据。如果我们将 Firefox 中的选项卡拖动到另一个位置,我们就会拖放图形组件。
QDrag
QDrag 提供基于 MIME 的拖放数据传输支持。它处理拖放操作的大部分细节。传输的数据包含在 QMimeData 对象中。
简单拖放示例
在第一个示例中,我们有一个 QLineEdit 和一个 QPushButton。我们将纯文本从行编辑窗口小部件拖放到按钮窗口小部件上。按钮的标签将会改变。
import sys
from PyQt6.QtWidgets import (QPushButton, QWidget,
QLineEdit, QApplication)
class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
self.setAcceptDrops(True)
def dragEnterEvent(self, e):
if e.mimeData().hasFormat('text/plain'):
e.accept()
else:
e.ignore()
def dropEvent(self, e):
self.setText(e.mimeData().text())
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
edit = QLineEdit('', self)
edit.setDragEnabled(True)
edit.move(30, 65)
button = Button("Button", self)
button.move(190, 65)
self.setWindowTitle('Simple drag and drop')
self.setGeometry(300, 300, 300, 150)
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec()
if __name__ == '__main__':
main()
该示例代表一个简单的拖放操作。
class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
...
为了将文本放到 QPushButton 小部件上,我们必须重新实现一些方法。因此,我们创建了自己的 Button 类,该类继承自 QPushButton 类。
self.setAcceptDrops(True)
我们使用 setAcceptDrops 为小部件启用放置事件。
def dragEnterEvent(self, e):
if e.mimeData().hasFormat('text/plain'):
e.accept()
else:
e.ignore()
首先,我们重新实现 dragEnterEvent 方法。我们告知我们接受的数据类型。在我们的例子中,它是纯文本。
def dropEvent(self, e):
self.setText(e.mimeData().text())
通过实现 dropEvent 方法,我们可以定义 drop 事件发生时会发生什么。这里我们更改按钮小部件的文本。
edit = QLineEdit('', self)
edit.setDragEnabled(True)
QLineEdit 控件内置了对拖拽操作的支持,我们需要做的就是调用 setDragEnabled 方法来激活它。
拖放按钮
下面的示例演示了如何拖放按钮小部件。
import sys
from PyQt6.QtCore import Qt, QMimeData
from PyQt6.QtGui import QDrag
from PyQt6.QtWidgets import QPushButton, QWidget, QApplication
class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
def mouseMoveEvent(self, e):
if e.buttons() != Qt.MouseButtons.RightButton:
return
mimeData = QMimeData()
drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.position().toPoint() - self.rect().topLeft())
dropAction = drag.exec(Qt.DropActions.MoveAction)
def mousePressEvent(self, e):
super().mousePressEvent(e)
if e.button() == Qt.MouseButtons.LeftButton:
print('press')
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
self.button = Button('Button', self)
self.button.move(100, 65)
self.setWindowTitle('Click or Move')
self.setGeometry(300, 300, 550, 450)
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
position = e.position()
self.button.move(position.toPoint())
e.setDropAction(Qt.DropActions.MoveAction)
e.accept()
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec()
if __name__ == '__main__':
main()
在我们的代码示例中,窗口上有一个 QPushButton。如果我们用鼠标左键单击该按钮,则“按下”消息将打印到控制台。通过右键单击并移动按钮,我们可以对按钮小部件执行拖放操作。
class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
我们创建一个从 QPushButton 派生的 Button 类。我们还重新实现了 QPushButton 的两个方法:mouseMoveEvent 和 mousePressEvent。mouseMoveEvent 方法是拖放操作开始的地方。
if e.buttons() != Qt.MouseButtons.RightButton:
return
这里我们决定只用鼠标右键来执行拖放操作。鼠标左键则用于单击按钮。
drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.position().toPoint() - self.rect().topLeft())
创建了 QDrag 对象。该类提供基于 MIME 的拖放数据传输支持。
dropAction = drag.exec(Qt.DropActions.MoveAction)
drag对象的exec方法启动拖放操作。
def mousePressEvent(self, e):
super().mousePressEvent(e)
if e.button() == Qt.MouseButtons.LeftButton:
print('press')
如果我们用鼠标左键单击按钮,我们会将“按下”打印到控制台。请注意,我们还在父级上调用 mousePressEvent 方法。否则,我们将看不到按钮被按下。
position = e.pos()
self.button.move(position)
在 dropEvent 方法中,我们指定释放鼠标按钮并完成放置操作后会发生什么。在我们的例子中,我们找出当前鼠标指针的位置并相应地移动按钮。
e.setDropAction(Qt.MoveAction)
e.accept()
我们用 setDropAction 指定放置动作的类型。在我们的例子中,它是一个移动动作。
绘画
PyQt6 绘画系统能够渲染矢量图形、图像和基于字体的文本轮廓。当我们想要更改或增强现有小部件时,或者如果我们要从头开始创建自定义小部件时,应用程序中就需要绘画。为了进行绘制,我们使用 PyQt6 工具包提供的绘画 API。
QPainter
QPainter 在小部件和其他绘画设备上执行低级绘画。它可以绘制从简单线条到复杂形状的所有内容。
paintEvent 方法
绘画是在 paintEvent 方法中完成的。绘画代码位于 QPainter 对象的 begin 和 end 方法之间。它在小部件和其他绘画设备上执行低级绘画。
绘制文本
我们首先在窗口的客户区域绘制一些 Unicode 文本。
import sys
from PyQt6.QtWidgets import QWidget, QApplication
from PyQt6.QtGui import QPainter, QColor, QFont
from PyQt6.QtCore import Qt
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.text = "Лев Николаевич Толстой\nАнна Каренина"
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Drawing text')
self.show()
def paintEvent(self, event):
qp = QPainter()
qp.begin(self)
self.drawText(event, qp)
qp.end()
def drawText(self, event, qp):
qp.setPen(QColor(168, 34, 3))
qp.setFont(QFont('Decorative', 10))
qp.drawText(event.rect(), Qt.Alignment.AlignCenter, self.text)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec())
if __name__ == '__main__':
main()
在我们的示例中,我们用西里尔字母绘制一些文本。文本垂直和水平对齐。
def paintEvent(self, event):
...
绘图是在绘画事件中完成的。
qp = QPainter()
qp.begin(self)
self.drawText(event, qp)
qp.end()
QPainter 类负责所有低级绘画。所有绘画方法都位于 begin 和 end 方法之间。实际绘画委托给 drawText 方法。
qp.setPen(QColor(168, 34, 3))
qp.setFont(QFont('Decorative', 10))
这里我们定义了一支笔和一个字体,用于绘制文本。
qp.drawText(event.rect(), Qt.Alignment.AlignCenter, self.text)
drawText 方法在窗口上绘制文本。paint 事件的 rect 方法返回需要更新的矩形。使用 Qt.Alignment.AlignCenter 我们可以对齐文本的两个维度。
绘制点
点是可以绘制的最简单的图形对象。它是窗口上的一个小点。
from PyQt6.QtWidgets import QWidget, QApplication
from PyQt6.QtGui import QPainter
from PyQt6.QtCore import Qt
import sys, random
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setMinimumSize(50, 50)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Points')
self.show()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawPoints(qp)
qp.end()
def drawPoints(self, qp):
qp.setPen(Qt.GlobalColor.red)
size = self.size()
for i in range(1000):
x = random.randint(1, size.width() - 1)
y = random.randint(1, size.height() - 1)
qp.drawPoint(x, y)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec())
if __name__ == '__main__':
main()
在我们的示例中,我们在窗口的客户区随机绘制 1000 个红点。
qp.setPen(Qt.GlobalColor.red)
我们将笔设置为红色。我们使用预定义的 Qt.GlobalColor.red 颜色常量。
size = self.size()
每次调整窗口大小时,都会生成绘制事件。我们使用 size 方法获取窗口的当前大小。我们使用窗口的大小将点分布在窗口的客户区各处。
qp.drawPoint(x, y)
我们使用 drawPoint 方法绘制点。
颜色
颜色是一个表示红色、绿色和蓝色 (RGB) 强度值组合的对象。有效的 RGB 值范围是 0 到 255。我们可以用各种方式定义颜色。最常见的是 RGB 十进制值或十六进制值。我们还可以使用代表红色、绿色、蓝色和 Alpha 的 RGBA 值。在这里我们添加了一些有关透明度的额外信息。Alpha 值 255 表示完全不透明度,0 表示完全透明,例如颜色不可见。
from PyQt6.QtWidgets import QWidget, QApplication
from PyQt6.QtGui import QPainter, QColor
import sys
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 350, 100)
self.setWindowTitle('Colours')
self.show()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawRectangles(qp)
qp.end()
def drawRectangles(self, qp):
col = QColor(0, 0, 0)
col.setNamedColor('#d4d4d4')
qp.setPen(col)
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)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec())
if __name__ == '__main__':
main()
在我们的示例中,我们绘制了三个彩色矩形。
color = QColor(0, 0, 0)
color.setNamedColor('#d4d4d4')
在这里,我们使用十六进制表示法定义颜色。
qp.setBrush(QColor(200, 0, 0))
qp.drawRect(10, 15, 90, 60)
在这里,我们定义画笔并绘制矩形。画笔是一个基本的图形对象,用于绘制形状的背景。 drawRect 方法接受四个参数。前两个是轴上的 x 和 y 值。第三和第四个参数是矩形的宽度和高度。该方法使用当前的笔和画笔绘制矩形。
QPen
QPen 是一个基本的图形对象。它用于绘制线条、曲线和矩形、椭圆形、多边形或其他形状的轮廓。
from PyQt6.QtWidgets import QWidget, QApplication
from PyQt6.QtGui import QPainter, QPen
from PyQt6.QtCore import Qt
import sys
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 280, 270)
self.setWindowTitle('Pen styles')
self.show()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawLines(qp)
qp.end()
def drawLines(self, qp):
pen = QPen(Qt.GlobalColor.black, 2, Qt.PenStyle.SolidLine)
qp.setPen(pen)
qp.drawLine(20, 40, 250, 40)
pen.setStyle(Qt.PenStyle.DashLine)
qp.setPen(pen)
qp.drawLine(20, 80, 250, 80)
pen.setStyle(Qt.PenStyle.DashDotLine)
qp.setPen(pen)
qp.drawLine(20, 120, 250, 120)
pen.setStyle(Qt.PenStyle.DotLine)
qp.setPen(pen)
qp.drawLine(20, 160, 250, 160)
pen.setStyle(Qt.PenStyle.DashDotDotLine)
qp.setPen(pen)
qp.drawLine(20, 200, 250, 200)
pen.setStyle(Qt.PenStyle.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
qp.setPen(pen)
qp.drawLine(20, 240, 250, 240)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec())
if __name__ == '__main__':
main()
在我们的示例中,我们绘制了六条线。这些线以六种不同的笔样式绘制。有五种预定义的笔样式。我们还可以创建自定义笔样式。最后一条线是使用自定义笔样式绘制的。
pen = QPen(Qt.GlobalColor.black, 2, Qt.PenStyle.SolidLine)
我们创建一个 QPen 对象。颜色为黑色。宽度设置为 2 像素,以便我们可以看到笔样式之间的差异。Qt.SolidLine 是预定义的笔样式之一。
pen.setStyle(Qt.PenStyle.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
qp.setPen(pen)
在这里,我们定义了一个自定义笔样式。我们设置了一个 Qt.PenStyle.CustomDashLine 笔样式并调用 setDashPattern 方法。数字列表定义了一种样式。数字必须是偶数。奇数定义虚线,偶数定义空格。数字越大,空格或虚线越大。我们的模式是 1 像素虚线、4 像素空格、5 像素虚线、4 像素空格等。
QBrush
QBrush 是一个基本的图形对象。它用于绘制图形形状(例如矩形、椭圆形或多边形)的背景。画笔可以分为三种类型:预定义画笔、渐变画笔或纹理图案。
from PyQt6.QtWidgets import QWidget, QApplication
from PyQt6.QtGui import QPainter, QBrush
from PyQt6.QtCore import Qt
import sys
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 355, 280)
self.setWindowTitle('Brushes')
self.show()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawBrushes(qp)
qp.end()
def drawBrushes(self, qp):
brush = QBrush(Qt.BrushStyle.SolidPattern)
qp.setBrush(brush)
qp.drawRect(10, 15, 90, 60)
brush.setStyle(Qt.BrushStyle.Dense1Pattern)
qp.setBrush(brush)
qp.drawRect(130, 15, 90, 60)
brush.setStyle(Qt.BrushStyle.Dense2Pattern)
qp.setBrush(brush)
qp.drawRect(250, 15, 90, 60)
brush.setStyle(Qt.BrushStyle.DiagCrossPattern)
qp.setBrush(brush)
qp.drawRect(10, 105, 90, 60)
brush.setStyle(Qt.BrushStyle.Dense5Pattern)
qp.setBrush(brush)
qp.drawRect(130, 105, 90, 60)
brush.setStyle(Qt.BrushStyle.Dense6Pattern)
qp.setBrush(brush)
qp.drawRect(250, 105, 90, 60)
brush.setStyle(Qt.BrushStyle.HorPattern)
qp.setBrush(brush)
qp.drawRect(10, 195, 90, 60)
brush.setStyle(Qt.BrushStyle.VerPattern)
qp.setBrush(brush)
qp.drawRect(130, 195, 90, 60)
brush.setStyle(Qt.BrushStyle.BDiagPattern)
qp.setBrush(brush)
qp.drawRect(250, 195, 90, 60)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec())
if __name__ == '__main__':
main()
在我们的例子中,我们画了九个不同的矩形。
brush = QBrush(Qt.BrushStyle.SolidPattern)
qp.setBrush(brush)
qp.drawRect(10, 15, 90, 60)
我们定义一个画笔对象。我们将其设置为画家对象,并通过调用 drawRect 方法绘制矩形。
贝塞尔曲线
贝塞尔曲线是一条三次线。PyQt6 中的贝塞尔曲线可以使用 QPainterPath 创建。画家路径是由许多图形构建块组成的对象,例如矩形、椭圆、直线和曲线。
import sys
from PyQt6.QtGui import QPainter, QPainterPath
from PyQt6.QtWidgets import QWidget, QApplication
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 380, 250)
self.setWindowTitle('Bézier curve')
self.show()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
qp.setRenderHint(QPainter.RenderHints.Antialiasing)
self.drawBezierCurve(qp)
qp.end()
def drawBezierCurve(self, qp):
path = QPainterPath()
path.moveTo(30, 30)
path.cubicTo(30, 30, 200, 350, 350, 30)
qp.drawPath(path)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec())
if __name__ == '__main__':
main()
此示例绘制了一条贝塞尔曲线。
path = QPainterPath()
path.moveTo(30, 30)
path.cubicTo(30, 30, 200, 350, 350, 30)
我们用 QPainterPath 路径创建一条贝塞尔曲线。该曲线使用 cubicTo 方法创建,该方法需要三个点:起点、控制点和终点。
qp.drawPath(path)
最终的路径用drawPath方法绘制。