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等
功能案例
这个控件,我们希望有两个方面的功能:
- 显示一个按百分比描述的量,例如电量,体现一种焦虑感;
- 通过鼠标交互来设置一个百分比显示的量,例如设置电量,体现一种控制感。
- 能够提供别的控件来设置这个百分比,例如滑块,体现一种便捷感。
- 能够构成动画,例如电量充电,体现一种活力感。
绘制方形图案
这里我们需要绘制一个方形的图案,这个图案的大小是根据控件的大小来自适应的,而且图案的颜色也是根据当前值来自适应的。这里我们需要用到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_())