程序中演示了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()