ui文件转换为python文件

方法一:直接使用命令行转换,demo.ui为保存的ui名,demo.py为ui转换为python的文件。

1 python -m PyQt5.uic.pyuic demo.ui -o demo.py

 

将py文件转换为ui文件

1 pyuic5 -o my_gui.py my_gui.ui

 

QLabel案例:使用信号

以下是QLabel控件的常用信号:

  • linkActivated: 当控件中包含超链接时,用户单击链接时触发此信号。
  • linkHovered: 当用户将鼠标悬停在超链接上时,触发此信号。
 1 import sys
 2 from PyQt5.QtWidgets import QApplication, QLabel
 3 from PyQt5.QtGui import QDesktopServices
 4 from PyQt5.QtCore import QUrl
 5 
 6 
 7 class Example(QLabel):
 8 
 9     def __init__(self):
10         super().__init__()
11 
12         self.setText('<a href="https://www.baidu.com/">百度</a>')
13         self.setOpenExternalLinks(True)
14         # 绑定到指定的事件函数。
15         # 鼠标悬浮停在超链接上后触发打开链接!
16         self.linkPressed.connect(self.openLink)
17 
18     def openLink(self, url):
19         QDesktopServices.openUrl(QUrl(url))
20 
21 
22 if __name__ == '__main__':
23     app = QApplication(sys.argv)
24     ex = Example()
25     ex.show()
26     sys.exit(app.exec_())

 

树(Tree Widget)控件

树控件的简单介绍

 1 树控件(Tree Widget)是一种常见的用户界面控件,它可以用来展示层次化的数据结构,例如文件系统、目录结构、组织结构等等。树控件通常由多个节点(Node)组成,每个节点都可以包含多个子节点。
 2 
 3 在PyQt5中,树控件是通过QTreeWidget类来实现的。下面是一些常用的树控件相关的概念:
 4 
 5 节点(Node):树控件中的基本元素,可以包含多个子节点。每个节点通常由一个图标、一段文本和一个可选的复选框组成。节点可以通过QTreeWidgetItem类来创建和操作。
 6 根节点(Root Node):树控件中的最顶层节点,它没有父节点。一个树控件通常只有一个根节点,但也可以有多个根节点。
 7 子节点(Child Node):节点的直接下级节点。
 8 父节点(Parent Node):节点的直接上级节点。根节点没有父节点。
 9 叶节点(Leaf Node):没有子节点的节点。
10 复选框(Checkbox):树控件中每个节点可以包含一个可选的复选框,用来表示节点的选中状态。
11 项数据(Item Data):每个节点都可以包含多个数据项,例如文本、图标、颜色等等。这些项数据可以通过QTreeWidgetItem.setData()和QTreeWidgetItem.data()方法来设置和获取。
12 展开(Expand):将一个节点的所有子节点显示出来。
13 折叠(Collapse):将一个节点的所有子节点隐藏起来。
14 
15 树控件通常具有以下特点:
16 
17 层次化结构:树控件中的节点可以是多层次的,可以有多个父节点和多个子节点。
18 可扩展性:树控件中的节点可以被动态地添加和删除,可以根据需要展开或折叠节点。
19 交互性:用户可以通过单击节点或复选框来选择或取消选择节点,可以通过双击节点或者右键菜单来进行编辑等操作。
20 可定制性:树控件中的节点可以设置不同的图标、颜色和字体等属性,以适应不同的需求和样式。
21 在PyQt5中,可以通过以下方法来操作树控件:
22 
23 添加节点:可以通过QTreeWidget.addTopLevelItem()方法或QTreeWidgetItem.addChild()方法来添加节点。
24 删除节点:可以通过QTreeWidget.takeTopLevelItem()方法或QTreeWidgetItem.removeChild()方法来删除节点。
25 获取节点:可以通过QTreeWidget.topLevelItem()方法或QTreeWidgetItem.child()方法来获取节点。
26 设置节点属性:可以通过QTreeWidgetItem.setText()、QTreeWidgetItem.setIcon()等方法来设置节点的文本、图标等属性。
27 选择节点:可以通过QTreeWidget.currentItem()方法获取当前选中的节点,也可以通过QTreeWidgetItem.setSelected

 

树控件综合案例分析

在这个案例中,我们创建了一个名为"组织结构图"的主窗口,并设置了固定的大小。然后,我们创建了一个树控件(QTreeWidget),并设置了树控件的列标题为"姓名"、“职位"和"部门”。

在populateTreeWidget()方法中,我们首先创建了根节点,并将其添加到树控件中。然后,我们创建了两个部门节点(department1和department2)作为根节点的子节点,以及每个部门节点下的员工节点。

每个节点的数据由一个字符串列表表示,其中包括员工的姓名、职位和部门信息。我们使用QTreeWidgetItem类来创建节点,并将其添加为父节点的子节点。

最后,我们展开了根节点,以便在组织结构图中显示默认的部门和员工关系。

 1 import sys
 2 from PyQt5.QtWidgets import QApplication, QMainWindow, QTreeWidget, QTreeWidgetItem
 3 
 4 class OrganizationChart(QMainWindow):
 5     def __init__(self):
 6         super().__init__()
 7 
 8         self.setWindowTitle("组织结构图")
 9         self.resize(500, 400)
10 
11         self.treeWidget = QTreeWidget()
12         self.treeWidget.setHeaderLabels(["姓名", "职位", "部门"])
13 
14         self.populateTreeWidget()
15 
16         self.setCentralWidget(self.treeWidget)
17 
18     def populateTreeWidget(self):
19         # 创建根节点
20         root = QTreeWidgetItem(self.treeWidget, ["总经理", "总经理", ""])
21 
22         # 创建部门节点1
23         department1 = QTreeWidgetItem(root, ["市场部", "部门经理", ""])
24         employee1 = QTreeWidgetItem(department1, ["张三", "销售经理", "市场部"])
25         employee2 = QTreeWidgetItem(department1, ["李四", "市场专员", "市场部"])
26 
27         # 创建部门节点2
28         department2 = QTreeWidgetItem(root, ["技术部", "部门经理", ""])
29         employee3 = QTreeWidgetItem(department2, ["王五", "技术总监", "技术部"])
30         employee4 = QTreeWidgetItem(department2, ["赵六", "开发工程师", "技术部"])
31 
32         # 展开根节点
33         self.treeWidget.expandItem(root)
34 
35 if __name__ == "__main__":
36     app = QApplication(sys.argv)
37 
38     organizationChart = OrganizationChart()
39     organizationChart.show()
40 
41     sys.exit(app.exec_())

 

图表主题动画

1.1效果展示

功能:

  • 支持不同的主题和动画效果。
  • 用户可以通过下拉框选择主题和动画效果,也可以勾选复选框来打开或关闭抗锯齿效果。
  • 创建了多个图表,包括区域图、柱状图、折线图、饼图、散点图和样条图。
  • 通过QChart,可以实现以下功能:

    创建图表对象:通过实例化QChart类,可以创建一个空的图表对象。
    添加数据系列:使用addSeries方法,可以向图表中添加数据系列。数据系列是图表的基本单位,代表一组相关的数据。
    设置图表标题和标签:使用setTitle和setLabels方法,可以设置图表的标题和标签。
    自定义轴设置:通过QValueAxis和QCategoryAxis类,可以对图表的轴进行自定义设置,包括刻度范围、标签格式、刻度间隔等。
    控制图表样式:通过setTheme方法,可以设置图表的整体样式主题,如亮色、暗色等。
    添加图例:使用legend属性,可以为图表添加图例,显示数据系列的标识和说明。
    支持交互操作:QChart提供了一些交互式操作的功能,如放大缩小、平移、数据点选取等,可以通过QChartView或其他图表视图类来实现。

  1 import random
  2 
  3 from PyQt5 import QtChart
  4 
  5 try:
  6     from PyQt5.QtChart import (QAreaSeries, QBarSet, QChart, QChartView,
  7                                QLineSeries, QPieSeries, QScatterSeries, QSplineSeries,
  8                                QStackedBarSeries)
  9     from PyQt5.QtCore import pyqtSlot, QPointF, Qt
 10     from PyQt5.QtGui import QColor, QPainter, QPalette
 11     from PyQt5.QtWidgets import QApplication, QMainWindow, QCheckBox, QComboBox, QGridLayout, QHBoxLayout, \
 12         QLabel, QSizePolicy, QWidget
 13 except ImportError:
 14     from PyQt5.QtChart import QChartView, QChart, QAreaSeries, QBarSet, QLineSeries, QPieSeries, QScatterSeries, \
 15         QSplineSeries, QStackedBarSeries  # 错误的导入方式,正确的导入方式在最上面
 16 
 17     QChartView = QtChart.QChartView
 18     QChart = QtChart.QChart
 19     QAreaSeries = QtChart.QAreaSeries
 20     QBarSet = QtChart.QBarSet
 21     QLineSeries = QtChart.QLineSeries
 22     QPieSeries = QtChart.QPieSeries
 23     QScatterSeries = QtChart.QScatterSeries
 24     QSplineSeries = QtChart.QSplineSeries
 25     QStackedBarSeries = QtChart.QStackedBarSeries
 26 
 27 
 28 class ThemeWidget(QWidget):
 29 
 30     def __init__(self, parent=None):
 31         super(ThemeWidget, self).__init__(parent)
 32 
 33         self.m_charts = []
 34         self.m_listCount = 3  # 数据个数
 35         self.m_valueMax = 10  # 每个数据点的最大值
 36         self.m_valueCount = 7  # 每个数据点的值个数
 37         self.m_dataTable = self.generateRandomData(self.m_listCount,
 38                                                    self.m_valueMax, self.m_valueCount)  # 调用generateRandomData方法生成随机数据
 39         self.m_themeComboBox = self.createThemeBox()  # 主题下拉框
 40         self.m_antialiasCheckBox = QCheckBox("Anti-aliasing")  # 抗锯齿复选框
 41         self.m_animatedComboBox = self.createAnimationBox()  # 动画下拉框
 42         self.m_legendComboBox = self.createLegendBox()  # 图例下拉框。
 43 
 44         self.connectSignals()
 45 
 46         baseLayout = QGridLayout()
 47         settingsLayout = QHBoxLayout()
 48         settingsLayout.addWidget(QLabel("Theme:"))
 49         settingsLayout.addWidget(self.m_themeComboBox)
 50         settingsLayout.addWidget(QLabel("Animation:"))
 51         settingsLayout.addWidget(self.m_animatedComboBox)
 52         settingsLayout.addWidget(QLabel("Legend:"))
 53         settingsLayout.addWidget(self.m_legendComboBox)
 54         settingsLayout.addWidget(self.m_antialiasCheckBox)
 55         settingsLayout.addStretch()
 56         baseLayout.addLayout(settingsLayout, 0, 0, 1, 3)
 57 
 58         # 创建图表
 59         chartView = QChartView(self.createAreaChart())
 60         baseLayout.addWidget(chartView, 1, 0)
 61         self.m_charts.append(chartView)
 62 
 63         # 创建柱状图
 64         chartView = QChartView(self.createBarChart(self.m_valueCount))
 65         baseLayout.addWidget(chartView, 1, 1)
 66         self.m_charts.append(chartView)
 67 
 68         # 创建折线图
 69         chartView = QChartView(self.createLineChart())
 70         baseLayout.addWidget(chartView, 1, 2)
 71         self.m_charts.append(chartView)
 72 
 73         # 创建饼图
 74         chartView = QChartView(self.createPieChart())
 75         chartView.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
 76         baseLayout.addWidget(chartView, 2, 0)
 77         self.m_charts.append(chartView)
 78 
 79         # 创建曲线图
 80         chartView = QChartView(self.createSplineChart())
 81         baseLayout.addWidget(chartView, 2, 1)
 82         self.m_charts.append(chartView)
 83 
 84         # 创建散点图
 85         chartView = QChartView(self.createScatterChart())
 86         baseLayout.addWidget(chartView, 2, 2)
 87         self.m_charts.append(chartView)
 88 
 89         self.setLayout(baseLayout)
 90 
 91         # Set the defaults.
 92         self.m_antialiasCheckBox.setChecked(True)
 93         self.updateUI()
 94 
 95     def connectSignals(self):
 96         """
 97         连接各种信号和槽,当主题下拉框、抗锯齿复选框、动画下拉框和图例下拉框的状态发生变化时,都会触发updateUI方法
 98         :return:
 99         """
100         self.m_themeComboBox.currentIndexChanged.connect(self.updateUI)
101         self.m_antialiasCheckBox.toggled.connect(self.updateUI)
102         self.m_animatedComboBox.currentIndexChanged.connect(self.updateUI)
103         self.m_legendComboBox.currentIndexChanged.connect(self.updateUI)
104 
105     def generateRandomData(self, listCount, valueMax, valueCount):
106         random.seed()
107         dataTable = []
108 
109         for i in range(listCount):
110             dataList = []
111             yValue = 0.0
112             f_valueCount = float(valueCount)
113 
114             for j in range(valueCount):
115                 yValue += random.uniform(0, valueMax) / f_valueCount
116                 value = QPointF(
117                     j + random.random() * self.m_valueMax / f_valueCount,
118                     yValue)
119                 label = "Slice " + str(i) + ":" + str(j)
120                 dataList.append((value, label))
121 
122             dataTable.append(dataList)
123 
124         return dataTable
125 
126     def createThemeBox(self):
127         """
128         创建主题下拉框
129         :return:
130         """
131         themeComboBox = QComboBox()
132 
133         themeComboBox.addItem("Light", QChart.ChartThemeLight)
134         themeComboBox.addItem("Blue Cerulean", QChart.ChartThemeBlueCerulean)
135         themeComboBox.addItem("Dark", QChart.ChartThemeDark)
136         themeComboBox.addItem("Brown Sand", QChart.ChartThemeBrownSand)
137         themeComboBox.addItem("Blue NCS", QChart.ChartThemeBlueNcs)
138         themeComboBox.addItem("High Contrast", QChart.ChartThemeHighContrast)
139         themeComboBox.addItem("Blue Icy", QChart.ChartThemeBlueIcy)
140 
141         return themeComboBox
142 
143     def createAnimationBox(self):
144         """
145         创建动画下拉框
146         :return:
147         """
148         animationComboBox = QComboBox()
149 
150         animationComboBox.addItem("No Animations", QChart.NoAnimation)
151         animationComboBox.addItem("GridAxis Animations", QChart.GridAxisAnimations)
152         animationComboBox.addItem("Series Animations", QChart.SeriesAnimations)
153         animationComboBox.addItem("All Animations", QChart.AllAnimations)
154 
155         return animationComboBox
156 
157     def createLegendBox(self):
158         """
159         创建图例下拉框
160         :return:
161         """
162         legendComboBox = QComboBox()
163 
164         legendComboBox.addItem("No Legend ", 0)
165         legendComboBox.addItem("Legend Top", Qt.AlignTop)
166         legendComboBox.addItem("Legend Bottom", Qt.AlignBottom)
167         legendComboBox.addItem("Legend Left", Qt.AlignLeft)
168         legendComboBox.addItem("Legend Right", Qt.AlignRight)
169 
170         return legendComboBox
171 
172     def createAreaChart(self):
173         """
174         创建面积图,遍历随机数据,创建上下两个序列,并将它们组合成面积图序列添加到图表中
175         :return:
176         """
177         chart = QChart()
178         chart.setTitle("Area chart")
179 
180         lowerSeries = None
181         y_points = []
182 
183         for i, data_list in enumerate(self.m_dataTable):
184             upperSeries = QLineSeries(chart)
185             for j, (value, _) in enumerate(data_list):
186                 y = value.y()
187 
188                 if lowerSeries is None:
189                     upperSeries.append(QPointF(j, y))
190                     y_points.append(y)
191                 else:
192                     new_y = y_points[i] + y
193                     upperSeries.append(QPointF(j, new_y))
194                     y_points[j] += new_y
195 
196             area = QAreaSeries(upperSeries, lowerSeries)
197             area.setName("Series " + str(i))
198             chart.addSeries(area)
199             lowerSeries = upperSeries
200 
201         chart.createDefaultAxes()
202 
203         return chart
204 
205     def createBarChart(self, valueCount):
206         chart = QChart()
207         chart.setTitle("Bar chart")
208 
209         series = QStackedBarSeries(chart)
210 
211         for i, data_list in enumerate(self.m_dataTable):
212             set = QBarSet("Bar set " + str(i))
213             for value, _ in data_list:
214                 set << value.y()
215 
216             series.append(set)
217 
218         chart.addSeries(series)
219         chart.createDefaultAxes()
220 
221         return chart
222 
223     def createLineChart(self):
224         chart = QChart()
225         chart.setTitle("Line chart")
226 
227         for i, data_list in enumerate(self.m_dataTable):
228             series = QLineSeries(chart)
229             for value, _ in data_list:
230                 series.append(value)
231 
232             series.setName("Series " + str(i))
233             chart.addSeries(series)
234 
235         chart.createDefaultAxes()
236 
237         return chart
238 
239     def createPieChart(self):
240         chart = QChart()
241         chart.setTitle("Pie chart")
242 
243         pieSize = 1.0 / len(self.m_dataTable)
244 
245         for i, data_list in enumerate(self.m_dataTable):
246             series = QPieSeries(chart)
247             for value, label in data_list:
248                 slice = series.append(label, value.y())
249                 if series.count() == 1:
250                     slice.setLabelVisible()
251                     slice.setExploded()
252 
253             hPos = (pieSize / 2) + (i / float(len(self.m_dataTable)))
254             series.setPieSize(pieSize)
255             series.setHorizontalPosition(hPos)
256             series.setVerticalPosition(0.5)
257 
258             chart.addSeries(series)
259 
260         return chart
261 
262     def createSplineChart(self):
263         chart = QChart()
264         chart.setTitle("Spline chart")
265 
266         for i, data_list in enumerate(self.m_dataTable):
267             series = QSplineSeries(chart)
268             for value, _ in data_list:
269                 series.append(value)
270 
271             series.setName("Series " + str(i))
272             chart.addSeries(series)
273 
274         chart.createDefaultAxes()
275 
276         return chart
277 
278     def createScatterChart(self):
279         chart = QChart()
280         chart.setTitle("Scatter chart")
281 
282         for i, data_list in enumerate(self.m_dataTable):
283             series = QScatterSeries(chart)
284             for value, _ in data_list:
285                 series.append(value)
286 
287             series.setName("Series " + str(i))
288             chart.addSeries(series)
289 
290         chart.createDefaultAxes()
291 
292         return chart
293 
294     @pyqtSlot()
295     def updateUI(self):
296         theme = self.m_themeComboBox.itemData(
297             self.m_themeComboBox.currentIndex())
298 
299         if self.m_charts[0].chart().theme() != theme:
300             for chartView in self.m_charts:
301                 chartView.chart().setTheme(QChart.ChartTheme(theme))
302 
303             pal = self.window().palette()
304 
305             if theme == QChart.ChartThemeLight:
306                 pal.setColor(QPalette.Window, QColor(0xf0f0f0))
307                 pal.setColor(QPalette.WindowText, QColor(0x404044))
308             elif theme == QChart.ChartThemeDark:
309                 pal.setColor(QPalette.Window, QColor(0x121218))
310                 pal.setColor(QPalette.WindowText, QColor(0xd6d6d6))
311             elif theme == QChart.ChartThemeBlueCerulean:
312                 pal.setColor(QPalette.Window, QColor(0x40434a))
313                 pal.setColor(QPalette.WindowText, QColor(0xd6d6d6))
314             elif theme == QChart.ChartThemeBrownSand:
315                 pal.setColor(QPalette.Window, QColor(0x9e8965))
316                 pal.setColor(QPalette.WindowText, QColor(0x404044))
317             elif theme == QChart.ChartThemeBlueNcs:
318                 pal.setColor(QPalette.Window, QColor(0x018bba))
319                 pal.setColor(QPalette.WindowText, QColor(0x404044))
320             elif theme == QChart.ChartThemeHighContrast:
321                 pal.setColor(QPalette.Window, QColor(0xffab03))
322                 pal.setColor(QPalette.WindowText, QColor(0x181818))
323             elif theme == QChart.ChartThemeBlueIcy:
324                 pal.setColor(QPalette.Window, QColor(0xcee7f0))
325                 pal.setColor(QPalette.WindowText, QColor(0x404044))
326             else:
327                 pal.setColor(QPalette.Window, QColor(0xf0f0f0))
328                 pal.setColor(QPalette.WindowText, QColor(0x404044))
329 
330             self.window().setPalette(pal)
331 
332         checked = self.m_antialiasCheckBox.isChecked()
333         for chartView in self.m_charts:
334             chartView.setRenderHint(QPainter.Antialiasing, checked)
335 
336         options = QChart.AnimationOptions(
337             self.m_animatedComboBox.itemData(
338                 self.m_animatedComboBox.currentIndex()))
339 
340         if self.m_charts[0].chart().animationOptions() != options:
341             for chartView in self.m_charts:
342                 chartView.chart().setAnimationOptions(options)
343 
344         alignment = self.m_legendComboBox.itemData(
345             self.m_legendComboBox.currentIndex())
346 
347         for chartView in self.m_charts:
348             legend = chartView.chart().legend()
349 
350             if alignment == 0:
351                 legend.hide()
352             else:
353                 legend.setAlignment(Qt.Alignment(alignment))
354                 legend.show()
355 
356 
357 if __name__ == '__main__':
358     import sys
359 
360     app = QApplication(sys.argv)
361 
362     window = QMainWindow()
363     widget = ThemeWidget()
364     window.setCentralWidget(widget)
365     window.resize(900, 600)
366     window.show()
367 
368     sys.exit(app.exec_())

 

PyQt5的动画框架

PyQt5的动画框架是QAbstractAnimation,它是一个抽象类,不能直接使用,需要使用它的子类。它的类结构如下:

QAbstractAnimation:抽象动画,是所有动画的基类,不能直接使用。
QVariantAnimation:值动画,用于改变控件的属性,比如改变控件的位置、大小、颜色等。
QPropertyAnimation:属性动画,用于改变控件的属性,比如改变控件的位置、大小、颜色等。
QAnimationGroup:动画组,可以包含多个动画,可以包含子动画组。
QSequentialAnimationGroup:顺序动画组,按照添加的顺序依次执行动画。
QParallelAnimationGroup:并行动画组,所有动画一起执行。
我们常用的,就是三个子类。

 1 QPropertyAnimation
 2 这个类的作用就是在一个Qt属性上定义一段动画。比如,我们可以在一个按钮上定义一个动画,让它的位置从(0, 0)移动到(100, 100)
 3 。那么这里的属性就是按钮的位置,动画的起始值是(0, 0),结束值是(100, 100)
 4 。这个属性在PyQt5里面定义的方式是采用@pyqtProperty(type signiture)和@property_name.setter
 5 
 6 
 7 QAnimationGroup
 8 这个类的两个子类,一个是QSequentialAnimationGroup,一个是QParallelAnimationGroup。前者是顺序执行动画,后者是并行执行动画。
 9 
10 pyqtProperty与插值
11 其实定义Qt Property在Python里面非常简单,只需要使用@pyqtProperty(type signiture)和@property_name.setter

当前,Qt内置了一些类的插值函数:
  • Int
  • UInt
  • Double
  • Float
  • QLine
  • QLineF
  • QPoint
  • QPointF
  • QSize
  • QSizeF
  • QColor
  • QRectF
  • QRect

如果你需要插值其他的类型,包括自定义类型,你必须自己实现插值。你可以注册一个插值函数,这个函数有三个参数:起始值,结束值,当前的进度。

最后就是一个动画的播放速度的,这个在Qt中称为QEasyCurve,它定义了在动画的播放过程中,时间和进度的关系。Qt内置了一些常用的曲线,比如:

Linear:线性
InQuad:初始点附近二次方
OutQuad:结束点二次方
InOutQuad:二次方
也可以我们自己定义

————————————————

原文链接:https://blog.csdn.net/withstand/article/details/130744774

案例:

 1 import sys
 2 
 3 from PyQt5.QtCore import *
 4 from PyQt5.QtGui import *
 5 from PyQt5.QtWidgets import *
 6 
 7 
 8 class ParentBackgroundAnimation(QObject):
 9 
10     def __init__(self, parent: QWidget = None):
11         super(ParentBackgroundAnimation, self).__init__(parent)
12         self._color = QColor(Qt.transparent)
13 
14     @pyqtProperty(QColor)
15     def color(self):
16         return self._color
17 
18     @color.setter
19     def color(self, color):
20         self._color = color
21         self.update()
22 
23     def update(self):
24         if self.parent is not None:
25             win: QWidget = self.parent()
26             win.setPalette(QPalette(self._color))
27 
28 
29 def make_animation_button(win: QWidget, layout: QLayout, animation_group: QAnimationGroup, params):
30     x0, y0, w0, h0, x1, y1, w1, h1, curve, label, time_ms = params
31     button = QPushButton(label, win)
32     if layout is not None:
33         layout.addWidget(button)
34 
35     animation = QPropertyAnimation(button, b"geometry")
36     animation.setDuration(time_ms)
37     animation.setStartValue(QRect(x0, y0, w0, h0))
38     animation.setEndValue(QRect(x1, y1, w1, h1))
39 
40     animation.setEasingCurve(curve)
41 
42     button.clicked.connect(animation_group.start)
43 
44     animation_group.addAnimation(animation)
45 
46     return button, animation
47 
48 
49 def color_animation(win, animation_group):
50     ti = ParentBackgroundAnimation(win)
51 
52     animation = QPropertyAnimation(ti, b"color")
53     animation.setDuration(2000)
54     animation.setStartValue(QColor(255, 0, 0, 100))
55     animation.setEndValue(QColor(0, 255, 0, 100))
56 
57     animation_group.addAnimation(animation)
58 
59 
60 if __name__ == "__main__":
61     app = QApplication([])
62 
63     win = QWidget()
64 
65     layout = None
66 
67     animation_group = QSequentialAnimationGroup()
68 
69     # 包含不同的缓动曲线类型(如线性、五次曲线等)
70     curves = [QEasingCurve.Linear, QEasingCurve.OutQuint, QEasingCurve.OutBounce, QEasingCurve.OutElastic,
71               QEasingCurve.OutBack, QEasingCurve.OutExpo]
72     # 
73     curve_labels = ["Linear", "OutQuint", "OutBounce", "OutElastic", "OutBack", "OutExpo"]
74 
75     for i, (c, l) in enumerate(zip(curves, curve_labels)):
76         sub_animation_group = QParallelAnimationGroup()
77         make_animation_button(win, layout, sub_animation_group, (
78             100 + i * 200, 10, 150, 80, 100 + i * 200, 600, 150, 80, c, l, 2000
79         ))
80         color_animation(win, sub_animation_group)
81         animation_group.addAnimation(sub_animation_group)
82 
83     animation_group.start()
84 
85     win.setWindowTitle("Animate buttons.")
86 
87     win.resize(1400, 700)
88 
89     win.show()
90 
91     sys.exit(app.exec_())

很好玩的动画下过,自己复制代码试试吧!!!!!!!!!!!!!!!!

 

界面布局基本支持

布局

布局是设计报表和交互中的重要工作。但是布局的内涵有两个层面上的:总体布局和界面布局。


从报表设计看,应将重要信息置于显眼处,围绕报表目的布局信息分类、汇总等功能以辅助决策。

从交互设计出发,核心功能放显眼位置,关联操作放一起,独立功能分开。实际设计可通过分组等方式实现总体布局。

较低层次是具体界面控件的定位、缩放、对齐等,可通过绝对位置布局(控件位置和尺寸)及相对位置布局(以某控件为基准转换坐标系)描述,更抽象布局可归结为相对和绝对布局算法。
 
 

  1 import random
  2 import sys
  3 from functools import partial
  4 from typing import Union
  5 
  6 from PyQt5.QtCore import Qt, QTimer
  7 from PyQt5.QtGui import QFont
  8 from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QFormLayout, \
  9     QStackedLayout, QComboBox, QLabel, QMainWindow, QDockWidget
 10 
 11 # module shared variables
 12 colors = ['yellow', 'red', 'cyan', 'green', 'white',
 13           'gray', 'orange', 'darkgray', 'transparent']
 14 name_layouts = {'vbox': QVBoxLayout,
 15                 'hbox': QHBoxLayout,
 16                 'grid': QGridLayout,
 17                 'form': QFormLayout,
 18                 'stacked': QStackedLayout}
 19 
 20 timer: Union[QTimer, None] = None
 21 
 22 
 23 # module shared functions
 24 def show_layout(parent: QMainWindow, layout_short_name: str):
 25     random.shuffle(colors)
 26 
 27     widgets = [QLabel(c, win) for c in colors]
 28 
 29     for i, (c, w) in enumerate(zip(colors, widgets)):
 30         w.setStyleSheet(f"background-color: {c}")
 31         w.setAlignment(Qt.AlignCenter)
 32         w.setFont(QFont("SimHei", 24))
 33 
 34     widget = QWidget(parent)
 35 
 36     if not (layout_short_name in name_layouts):
 37         raise ValueError(
 38             f"{layout_short_name} not in {list(name_layouts.keys())}")
 39     layout = name_layouts[layout_short_name]()
 40 
 41     for i, w in enumerate(widgets):
 42         if layout_short_name == 'grid':
 43             layout.addWidget(w, i // 3, i % 3)
 44         elif layout_short_name == 'form':
 45             layout.addRow(w.text() + ":", w)
 46         else:
 47             layout.addWidget(w)
 48 
 49     global timer
 50     if timer is not None:
 51         timer.stop()
 52         timer = None
 53 
 54     if layout_short_name == 'stacked':
 55         timer = QTimer(parent)
 56 
 57         def change_layout():
 58             try:
 59                 layout.setCurrentIndex((layout.currentIndex() + 1) % layout.count())
 60             except Exception as e:
 61                 pass
 62 
 63         timer.timeout.connect(change_layout)
 64         timer.start(1000)
 65 
 66     widget.setLayout(layout)
 67     parent.setCentralWidget(widget)
 68 
 69 
 70 class QCycleComboBox(QComboBox):
 71     """
 72     A combobox that cycles through its items when the mouse wheel is used, or the up/down keys are pressed.
 73     """
 74 
 75     def __init__(self, parent: QWidget = None) -> None:
 76         super(QCycleComboBox, self).__init__(parent)
 77 
 78     def wheelEvent(self, event):
 79         if event.angleDelta().y() > 0:
 80             self.setCurrentIndex((self.currentIndex() - 1) % self.count())
 81         else:
 82             self.setCurrentIndex((self.currentIndex() + 1) % self.count())
 83 
 84     def keyPressEvent(self, e) -> None:
 85         if e.key() == Qt.Key_Up:
 86             self.setCurrentIndex((self.currentIndex() - 1) % self.count())
 87         if e.key() == Qt.Key_Down:
 88             self.setCurrentIndex((self.currentIndex() + 1) % self.count())
 89         else:
 90             return super(QCycleComboBox, self).keyPressEvent(e)
 91 
 92 
 93 if __name__ == '__main__':
 94     app = QApplication([])
 95     win = QMainWindow()
 96     
 97     choice = QCycleComboBox(win)
 98     choice.addItems(name_layouts.keys())
 99     dock = QDockWidget('Layouts selection', win)
100     dock.setWidget(choice)
101     dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
102     win.addDockWidget(Qt.TopDockWidgetArea, dock)
103     choice.currentTextChanged.connect(partial(show_layout, win))
104     show_layout(win, 'vbox')
105 
106     win.setWindowTitle("Layouts")
107     win.setGeometry(100, 100, 800, 600)
108     win.show()
109 
110     sys.exit(app.exec_())

 

 

定制控件-界面设计

QWidget是Qt中所有界面控件的基类,它提供了一些基本的功能,比如绘图、事件处理、布局管理等。我们可以通过继承QWidget来实现自己的界面控件

要实现一个自定义的界面,就需要做下面几件事情:

继承QWidget类,实现自己的界面控件
重写paintEvent函数,实现绘图
重写resizeEvent函数,实现界面大小变化时的处理,或者设置sizePolicy和sizeHint函数,实现界面大小的控制
重写mousePressEvent、mouseMoveEvent、mouseReleaseEvent函数,实现鼠标事件的处理
重写keyPressEvent函数,实现键盘事件的处理
重写timerEvent函数,实现定时器事件的处理
重写其他事件处理函数,比如focusInEvent、focusOutEvent、enterEvent、leaveEvent等
重写函数showEvent、hideEvent、closeEvent等
重写函数setGeometry、setFixedSize、setMinimumSize、setMaximumSize等
重写函数setStyleSheet、setCursor、setToolTip等
重写setFocusPolicy、setFocus、clearFocus等

功能案例

这个控件,我们希望有两个方面的功能:

  1. 显示一个按百分比描述的量,例如电量,体现一种焦虑感;
  2. 通过鼠标交互来设置一个百分比显示的量,例如设置电量,体现一种控制感。
  3. 能够提供别的控件来设置这个百分比,例如滑块,体现一种便捷感。
  4. 能够构成动画,例如电量充电,体现一种活力感。

 

绘制方形图案
这里我们需要绘制一个方形的图案,这个图案的大小是根据控件的大小来自适应的,而且图案的颜色也是根据当前值来自适应的。这里我们需要用到QPainter的几个函数:

QPainter::fillRect:填充矩形
QPainter::setBrush:设置画刷
QPainter::setPen:设置画笔
QPainter::setFont: 设置字体
QPainter::drawText: 绘制字符串

  1 import sys
  2 
  3 from PyQt5 import QtCore, QtGui
  4 from PyQt5.QtCore import Qt, pyqtSlot, pyqtProperty, pyqtSignal, QRectF, QSize, QPropertyAnimation
  5 from PyQt5.QtGui import QColor, QBrush, QPainter
  6 from PyQt5.QtWidgets import QWidget, QSizePolicy, QApplication
  7 
  8 
  9 class PowerBar(QWidget):
 10     """
 11     Custom Qt Widget to show a power bar.
 12     Demonstrating compound and custom-drawn widget.
 13     """
 14 
 15     def __init__(self, steps=5, *args, **kwargs):
 16         super().__init__(*args, **kwargs)
 17         self._value = 0
 18         self._minimum = 0
 19         self._maximum = 100
 20 
 21         self.setSizePolicy(
 22             QSizePolicy.MinimumExpanding,
 23             QSizePolicy.MinimumExpanding
 24         )
 25 
 26         if isinstance(steps, list):
 27             # list of colors.
 28             self.n_steps = len(steps)
 29             self.steps = steps
 30 
 31         elif isinstance(steps, int):
 32             # int number of bars, defaults to red.
 33             self.n_steps = steps
 34             self.steps = ['red'] * steps
 35 
 36         else:
 37             raise TypeError('steps must be a list or int')
 38 
 39         self._bar_solid_percent = 0.8
 40         self._background_color = QColor('black')
 41         self._padding = 4.0  # n-pixel gap around edge.
 42 
 43     def paintEvent(self, e):
 44         painter = QPainter(self)
 45 
 46         brush = QBrush()
 47         brush.setColor(self._background_color)
 48         brush.setStyle(Qt.SolidPattern)
 49         rect = QtCore.QRect(0, 0, painter.device().width(), painter.device().height())
 50         painter.fillRect(rect, brush)
 51 
 52         # Get current state.
 53         vmin, vmax = self._minimum, self._maximum
 54         value = self.value
 55 
 56         # Define our canvas.
 57         d_height = painter.device().height() - (self._padding * 2)
 58         d_width = painter.device().width() - (self._padding * 2)
 59 
 60         # Draw the bars.
 61         step_size = d_height / self.n_steps
 62         bar_height = step_size * self._bar_solid_percent
 63         bar_spacer = step_size * (1 - self._bar_solid_percent) / 2
 64 
 65         # Calculate the y-stop position, from the value in range.
 66         pc = (value - vmin) / (vmax - vmin)
 67         n_steps_to_draw = int(pc * self.n_steps)
 68 
 69         for n in range(n_steps_to_draw):
 70             brush.setColor(QtGui.QColor(self.steps[n]))
 71             rect = QtCore.QRectF(
 72                 self._padding,
 73                 self._padding + d_height - ((1 + n) * step_size) + bar_spacer,
 74                 d_width,
 75                 bar_height
 76             )
 77             painter.fillRect(rect, brush)
 78 
 79         # draw text in the midddle of the bar
 80         painter.setPen(QColor('white'))
 81         rect = QRectF(
 82             self._padding,
 83             self._padding,
 84             d_width - self._padding,
 85             d_height - self._padding
 86         )
 87         # change font size to 20
 88         font = painter.font()
 89         font.setPointSize(18)
 90         font.setFamily("Arial")
 91         font.setBold(True)
 92         painter.setFont(font)
 93         painter.drawText(rect, Qt.AlignCenter, f"{value}%")
 94         painter.end()
 95 
 96     def sizeHint(self):
 97         return QSize(30, 120)
 98 
 99     def _trigger_refresh(self):
100         self.update()
101 
102     def _calculate_clicked_value(self, e):
103         min_val, max_val = self._minimum, self._maximum
104         d_height = self.size().height() + (self._padding * 2)
105         step_size = d_height / self.n_steps
106         click_y = e.y() - self._padding - step_size / 2
107 
108         pc = (d_height - click_y) / d_height
109         value = min_val + pc * (max_val - min_val)
110         if value > self._maximum:
111             value = self._maximum
112         if value < self._minimum:
113             value = self._minimum
114         self.value = int(value)
115 
116     def mouseMoveEvent(self, e):
117         self._calculate_clicked_value(e)
118 
119     def mousePressEvent(self, e):
120         self._calculate_clicked_value(e)
121 
122     def setColor(self, color):
123         self.steps = [color] * self.n_steps
124         self.update()
125 
126     def setColors(self, colors):
127         self.n_steps = len(colors)
128         self.steps = colors
129         self.update()
130 
131     def setBarPadding(self, i):
132         self._padding = int(i)
133         self.update()
134 
135     def setBarSolidPercent(self, f):
136         self._bar_solid_percent = float(f)
137         self.update()
138 
139     def setBackgroundColor(self, color):
140         self._background_color = QColor(color)
141         self.update()
142 
143     # signal and slots framework
144     valueChanged = pyqtSignal(int)
145 
146     @pyqtSlot(int)
147     def setValue(self, value):
148         self.value = value
149 
150     @pyqtProperty(int)
151     def value(self):
152         return self._value
153 
154     @value.setter
155     def value(self, value):
156         self._value = value
157         self.update()
158         self.valueChanged.emit(value)
159 
160 
161 if __name__ == '__main__':
162     app = QApplication([])
163     volume = PowerBar([QColor(255, 255 - i, 0) for i in range(0, 255, 15)])
164 
165     # set volume window without minimize and maximize button
166     volume.setWindowFlags(Qt.WindowCloseButtonHint)
167 
168     volume.resize(100, 500)
169     anim = QPropertyAnimation(volume, b"value")
170     anim.setDuration(5000)
171     anim.setStartValue(0)
172     anim.setKeyValueAt(0.8, 100)
173     anim.setEndValue(0)
174 
175     volume.show()
176 
177     anim.finished.connect(lambda: volume.valueChanged.connect(lambda: volume.setWindowTitle(f"{volume.value}%")))
178 
179     anim.start()
180 
181     sys.exit(app.exec_())

 

posted on 2024-09-14 10:52  认真的六六  阅读(82)  评论(0编辑  收藏  举报