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 >百度</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_())
浙公網安備 33010602011771號