程序中演示了PyQt中編程實現自定義圓形指示燈控件的方法,通過程序界面可改變其屬性值並能實時看到指示燈的外觀變化情況,同時,在定時器中也實現了一個類似跑馬燈效果的功能。
例子雖小,但涉及的編程點挺多,包括:自定義控件(圓形指示燈、顏色選擇框等)、分割條、布局、輻射漸變畫刷、定時器、自定義信號(pyqtSignal)、列表、setattr、hasattr、for...in、zip、map、十進制轉二進制等等。
代碼如下:
1 # -*- coding: utf-8 -*-# 2 3 #------------------------------------------------------------------------------- 4 # Name: 自定義圓形指示燈控件 5 # Description: 6 # Author: lgk 7 # Date: 2018/7/05 8 #------------------------------------------------------------------------------- 9 10 import sys 11 from PyQt4.QtGui import * 12 from PyQt4.QtCore import * 13 14 allAttributes = [ 'colorOnBegin', 'colorOnEnd', 'colorOffBegin', 'colorOffEnd', 'colorBorderIn', 'colorBorderOut', 15 'radiusBorderOut', 'radiusBorderIn', 'radiusCircle'] 16 allDefaultVal = [ QColor(0, 240, 0), QColor(0, 160, 0), QColor(0, 68, 0), QColor(0, 28, 0), QColor(140, 140, 140), QColor(100, 100, 100), 17 500, 450, 400] 18 allLabelNames = [ u'燈亮圓心顏色:', u'燈亮邊緣顏色:', u'燈滅圓心顏色:', u'燈滅邊緣顏色:', u'邊框內測顏色:', u'邊框外側顏色:', 19 u'邊框外側半徑:', u'邊框內側半徑:', u'中間圓燈半徑:'] 20 21 class MyLed(QAbstractButton): 22 def __init__(self, parent=None): 23 super(MyLed, self).__init__(parent) 24 self.initUI() 25 26 def initUI(self): 27 self.setMinimumSize(24, 24) 28 self.setCheckable(True) 29 self.scaledSize = 1000.0 #為方便計算,將窗口短邊值映射為1000 30 self.setLedDefaultOption() 31 32 def setLedDefaultOption(self): 33 for attr, val in zip(allAttributes, allDefaultVal): 34 setattr(self, attr, val) 35 self.update() 36 37 def setLedOption(self, opt='colorOnBegin', val=QColor(0,240,0)): 38 if hasattr(self, opt): 39 setattr(self, opt, val) 40 self.update() 41 42 def resizeEvent(self, evt): 43 self.update() 44 45 def paintEvent(self, evt): 46 painter = QPainter(self) 47 painter.setRenderHint(QPainter.Antialiasing, True) 48 painter.setPen(QPen(Qt.black, 1)) 49 50 realSize = min(self.width(), self.height()) #窗口的短邊 51 painter.translate(self.width()/2.0, self.height()/2.0) #原點平移到窗口中心 52 painter.scale(realSize/self.scaledSize, realSize/self.scaledSize) #縮放,窗口的短邊值映射為self.scaledSize 53 gradient = QRadialGradient(QPointF(0, 0), self.scaledSize/2.0, QPointF(0, 0)) #輻射漸變 54 55 #畫邊框外圈和內圈 56 for color, radius in [(self.colorBorderOut, self.radiusBorderOut), #邊框外圈 57 (self.colorBorderIn, self.radiusBorderIn)]: #邊框內圈 58 gradient.setColorAt(1, color) 59 painter.setBrush(QBrush(gradient)) 60 painter.drawEllipse(QPointF(0, 0), radius, radius) 61 62 # 畫內圓 63 gradient.setColorAt(0, self.colorOnBegin if self.isChecked() else self.colorOffBegin) 64 gradient.setColorAt(1, self.colorOnEnd if self.isChecked() else self.colorOffEnd) 65 painter.setBrush(QBrush(gradient)) 66 painter.drawEllipse(QPointF(0, 0), self.radiusCircle, self.radiusCircle) 67 68 class MyColorBox(QFrame): 69 sigColorChanged = pyqtSignal(QColor) 70 def __init__(self, parent=None, height=20, color=QColor(0,240,0)): 71 super(MyColorBox, self).__init__(parent) 72 self.setFixedHeight(height) 73 self.setAutoFillBackground(True) 74 self.setPalette(QPalette(color)) 75 self.setFrameStyle(QFrame.Panel | QFrame.Sunken) 76 77 def mousePressEvent(self, *args, **kwargs): 78 color = QColorDialog.getColor(initial=self.palette().color(QPalette.Window)) 79 if color.isValid(): 80 self.setPalette(QPalette(color)) 81 self.sigColorChanged.emit(color) 82 83 def setColor(self, color): 84 self.setPalette(QPalette(color)) 85 86 class MyRadiusCtrl(QSpinBox): 87 def __init__(self, parent=None, initVal=500): 88 super(MyRadiusCtrl, self).__init__(parent) 89 self.setRange(1, 500) 90 self.setValue(initVal) 91 92 class ConfigWnd(QFrame): 93 def __init__(self, parent=None): 94 super(ConfigWnd, self).__init__(parent) 95 self.initUI() 96 97 def initUI(self): 98 self.setFrameStyle(QFrame.Box|QFrame.Sunken) 99 100 mainLayout = QVBoxLayout(self) 101 mainLayout.addWidget(self.createColorParaGroupBox(), 0) 102 mainLayout.addSpacing(20) 103 mainLayout.addWidget(self.createRadiusParaGroupBox(), 0) 104 mainLayout.addStretch() 105 mainLayout.addSpacing(20) 106 self.restoreDefaultBtn = QPushButton(u'恢復默認設置') 107 mainLayout.addWidget(self.restoreDefaultBtn, 0) 108 mainLayout.addSpacing(10) 109 self.animateBtn = QPushButton(u'開始動畫') 110 self.animateBtn.setCheckable(True) 111 mainLayout.addWidget(self.animateBtn, 0) 112 113 def createColorParaGroupBox(self): 114 colorParaGroupBox = QGroupBox(u"顏色參數設置", self) 115 layout = QGridLayout(colorParaGroupBox) 116 layout.setSpacing(10) 117 118 self.allColorBoxCtrls = [] 119 for name, color, row in zip(allLabelNames[:6], allDefaultVal[:6], range(6)): 120 layout.addWidget(QLabel(name), row, 0) 121 colorBox = MyColorBox(color=color) 122 layout.addWidget(colorBox, row, 1) 123 self.allColorBoxCtrls.append(colorBox) 124 125 layout.setColumnStretch(0, 0) 126 layout.setColumnStretch(1, 1) 127 return colorParaGroupBox 128 129 def createRadiusParaGroupBox(self): 130 radiusParaGroupBox = QGroupBox(u"半徑設置(1~500)", self) 131 layout = QGridLayout(radiusParaGroupBox) 132 layout.setSpacing(10) 133 134 self.allRadiusCtrls = [] 135 for name, radius, row in zip(allLabelNames[6:], allDefaultVal[6:], range(3)): 136 layout.addWidget(QLabel(name), row, 0) 137 radiusCtrl = MyRadiusCtrl(initVal=radius) 138 layout.addWidget(radiusCtrl, row, 1) 139 self.allRadiusCtrls.append(radiusCtrl) 140 141 layout.setColumnStretch(0, 0) 142 layout.setColumnStretch(1, 1) 143 return radiusParaGroupBox 144 145 class MainWindow(QMainWindow): 146 def __init__(self): 147 super(MainWindow, self).__init__() 148 self.initUI() 149 self.initSlotFunc() 150 self.cnt = 0 151 self.show() 152 153 def initUI(self): 154 self.resize(580, 350) 155 self.setWindowTitle(u'自定義圓形指示燈控件') 156 157 mainSplitter = self.createSplitter(style=Qt.Horizontal, parent=self, width=4) 158 159 self.configWnd = ConfigWnd(mainSplitter) 160 161 rightSplitter = self.createSplitter(style=Qt.Vertical, parent=mainSplitter, width=4) 162 163 rightTopWnd = self.createSubWnd(rightSplitter) 164 rightTopLayout = QVBoxLayout(rightTopWnd) 165 rightTopLayout.setContentsMargins(60, 60, 60, 60) 166 self.ledSingle = MyLed() 167 self.ledSingle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 168 rightTopLayout.addWidget(self.ledSingle) 169 170 rightBottomWnd = self.createSubWnd(rightSplitter) 171 rightBottomLayout = QHBoxLayout(rightBottomWnd) 172 rightBottomLayout.setContentsMargins(10, 10, 10, 10) 173 self.ledGroup = [] 174 for i in range(8): 175 led = MyLed() 176 led.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 177 self.ledGroup.append(led) 178 rightBottomLayout.addWidget(led) 179 180 self.setSplitterStrechFactor(rightSplitter, 1, 0) 181 self.setSplitterStrechFactor(mainSplitter, 0, 1) 182 self.setCentralWidget(mainSplitter) 183 184 def createSplitter(self, style=Qt.Horizontal, parent=None, width=3): 185 splitter = QSplitter(style, parent) 186 splitter.setHandleWidth(width) 187 return splitter 188 189 def setSplitterStrechFactor(self, splitter=None, factor1=1, factor2=1): #設置分割條兩部分的比例 190 splitter.setStretchFactor(0, factor1) 191 splitter.setStretchFactor(1, factor2) 192 193 def createSubWnd(self, parent=None): 194 wnd = QFrame(parent) 195 wnd.setFrameStyle(QFrame.Box | QFrame.Sunken) 196 return wnd 197 198 def initSlotFunc(self): 199 self.configWnd.restoreDefaultBtn.clicked.connect(self.slotRestoreDefault) 200 map(lambda x: x.sigColorChanged.connect(self.slotattributeChanged), self.configWnd.allColorBoxCtrls) #設定每個顏色控件的槽函數 201 map(lambda x: x.valueChanged.connect(self.slotattributeChanged), self.configWnd.allRadiusCtrls) #設定每個半徑控件的槽函數 202 self.configWnd.animateBtn.clicked.connect(self.slotAnimation) 203 self.timer = QTimer() 204 self.timer.timeout.connect(self.slotTimeout) #動畫定時器 205 206 def slotattributeChanged(self, val): 207 allCtrls = self.configWnd.allColorBoxCtrls + self.configWnd.allRadiusCtrls 208 idx = allCtrls.index(self.sender()) 209 self.ledSingle.setLedOption(allAttributes[idx], val) 210 211 def slotRestoreDefault(self): 212 for colorBox, val in zip(self.configWnd.allColorBoxCtrls, allDefaultVal[:6]): 213 colorBox.setColor(val) 214 215 for radiusCtrl, val in zip(self.configWnd.allRadiusCtrls, allDefaultVal[6:]): 216 radiusCtrl.setValue(val) 217 218 self.ledSingle.setLedDefaultOption() 219 220 def slotAnimation(self): 221 if self.configWnd.animateBtn.isChecked(): 222 self.cnt = 0 223 self.configWnd.animateBtn.setText(u'停止動畫') 224 self.timer.start(300) 225 else: 226 self.configWnd.animateBtn.setText(u'開始動畫') 227 self.timer.stop() 228 229 def slotTimeout(self): 230 self.cnt = self.cnt % 256 231 ledBits = QString('%1').arg(self.cnt, 8, 2, fillChar=QChar('0')) #將數值轉換為二進制字符串 232 for ledBit, led in zip(ledBits, self.ledGroup): 233 led.setChecked(ledBit=='1') 234 self.cnt += 1 235 236 def main(): 237 app = QApplication(sys.argv) 238 mainWnd = MainWindow() 239 sys.exit(app.exec_()) 240 241 if __name__ == '__main__': 242 main()