程序中演示了PyQt中编程实现自定义圆形指示灯控件的方法,通过程序界面可改变其属性值并能实时看到指示灯的外观变化情况,同时,在定时器中也实现了一个类似跑马灯效果的功能。
例子虽小,但涉及的编程点挺多,包括:自定义控件(圆形指示灯、颜色选择框等)、分割条、布局、辐射渐变画刷、定时器、自定义信号(pyqtSignal)、列表、字典、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 allColorBoxInfo = [ {'label':u'灯亮圆心颜色:', 'name':'colorOnBegin', 'defaultColor':QColor(0, 240, 0)}, 15 {'label':u'灯亮边缘颜色:', 'name':'colorOnEnd', 'defaultColor':QColor(0, 160, 0)}, 16 {'label':u'灯灭圆心颜色:', 'name':'colorOffBegin', 'defaultColor':QColor(0, 68, 0)}, 17 {'label':u'灯灭边缘颜色:', 'name':'colorOffEnd', 'defaultColor':QColor(0, 28, 0)}, 18 {'label':u'边框内测颜色:', 'name':'colorBorderIn', 'defaultColor':QColor(140, 140, 140)}, 19 {'label':u'边框外侧颜色:', 'name':'colorBorderOut', 'defaultColor':QColor(100, 100, 100)} 20 ] 21 allRadiusInfo = [ {'label':u'边框外侧半径:', 'name':'radiusBorderOut', 'defaultRadius':500}, 22 {'label':u'边框内侧半径:', 'name':'radiusBorderIn', 'defaultRadius': 450}, 23 {'label':u'中间圆灯半径:', 'name':'radiusCircle', 'defaultRadius': 400} 24 ] 25 26 class MyLed(QAbstractButton): 27 def __init__(self, parent=None): 28 super(MyLed, self).__init__(parent) 29 self.initUI() 30 31 def initUI(self): 32 self.setMinimumSize(24, 24) 33 self.setCheckable(True) 34 self.scaledSize = 1000.0 #为方便计算,将窗口短边值映射为1000 35 self.setLedDefaultOption() 36 37 def setLedDefaultOption(self): 38 for name, val in zip([d['name'] for d in allColorBoxInfo], [d['defaultColor'] for d in allColorBoxInfo]): 39 setattr(self, name, val) 40 for name, val in zip([d['name'] for d in allRadiusInfo], [d['defaultRadius'] for d in allRadiusInfo]): 41 setattr(self, name, val) 42 self.update() 43 44 def setLedOption(self, opt='colorOnBegin', val=QColor(0,240,0)): 45 if hasattr(self, opt): 46 setattr(self, opt, val) 47 self.update() 48 49 def resizeEvent(self, evt): 50 self.update() 51 52 def paintEvent(self, evt): 53 painter = QPainter(self) 54 painter.setRenderHint(QPainter.Antialiasing, True) 55 painter.setPen(QPen(Qt.black, 1)) 56 57 realSize = min(self.width(), self.height()) #窗口的短边 58 painter.translate(self.width()/2.0, self.height()/2.0) #原点平移到窗口中心 59 painter.scale(realSize/self.scaledSize, realSize/self.scaledSize) #缩放,窗口的短边值映射为self.scaledSize 60 gradient = QRadialGradient(QPointF(0, 0), self.scaledSize/2.0, QPointF(0, 0)) #辐射渐变 61 62 #画边框外圈和内圈 63 for color, radius in [(self.colorBorderOut, self.radiusBorderOut), #边框外圈 64 (self.colorBorderIn, self.radiusBorderIn)]: #边框内圈 65 gradient.setColorAt(1, color) 66 painter.setBrush(QBrush(gradient)) 67 painter.drawEllipse(QPointF(0, 0), radius, radius) 68 69 # 画内圆 70 gradient.setColorAt(0, self.colorOnBegin if self.isChecked() else self.colorOffBegin) 71 gradient.setColorAt(1, self.colorOnEnd if self.isChecked() else self.colorOffEnd) 72 painter.setBrush(QBrush(gradient)) 73 painter.drawEllipse(QPointF(0, 0), self.radiusCircle, self.radiusCircle) 74 75 class MyColorBox(QFrame): 76 sigColorChanged = pyqtSignal(QColor) 77 def __init__(self, parent=None, height=20, color=QColor(0,240,0), name=''): 78 super(MyColorBox, self).__init__(parent) 79 self.setFixedHeight(height) 80 self.setAutoFillBackground(True) 81 self.setPalette(QPalette(color)) 82 self.setFrameStyle(QFrame.Panel | QFrame.Sunken) 83 self.name = name 84 85 def mousePressEvent(self, *args, **kwargs): 86 color = QColorDialog.getColor(initial=self.palette().color(QPalette.Window)) 87 if color.isValid(): 88 self.setPalette(QPalette(color)) 89 self.sigColorChanged.emit(color) 90 91 def setColor(self, color): 92 self.setPalette(QPalette(color)) 93 94 class MyRadiusCtrl(QSpinBox): 95 def __init__(self, parent=None, name='', initVal=500): 96 super(MyRadiusCtrl, self).__init__(parent) 97 self.name = name 98 self.setRange(1, 500) 99 self.setValue(initVal) 100 101 class ConfigWnd(QFrame): 102 def __init__(self, parent=None): 103 super(ConfigWnd, self).__init__(parent) 104 self.initUI() 105 106 def initUI(self): 107 self.setFrameStyle(QFrame.Box|QFrame.Sunken) 108 109 mainLayout = QVBoxLayout(self) 110 mainLayout.addWidget(self.createColorParaGroupBox(), 0) 111 mainLayout.addSpacing(20) 112 mainLayout.addWidget(self.createRadiusParaGroupBox(), 0) 113 mainLayout.addStretch() 114 mainLayout.addSpacing(20) 115 self.restoreDefaultBtn = QPushButton(u'恢复默认设置') 116 mainLayout.addWidget(self.restoreDefaultBtn, 0) 117 mainLayout.addSpacing(10) 118 self.animateBtn = QPushButton(u'开始动画') 119 self.animateBtn.setCheckable(True) 120 mainLayout.addWidget(self.animateBtn, 0) 121 122 def createColorParaGroupBox(self): 123 colorParaGroupBox = QGroupBox(u"颜色参数设置", self) 124 layout = QGridLayout(colorParaGroupBox) 125 layout.setSpacing(10) 126 127 self.allColorBoxCtrls = {} 128 for eachColorBoxInfo, row in zip(allColorBoxInfo, range(len(allColorBoxInfo))): 129 layout.addWidget(QLabel(eachColorBoxInfo['label']), row, 0) 130 colorBox = MyColorBox(color=eachColorBoxInfo['defaultColor'], name=eachColorBoxInfo['name']) 131 layout.addWidget(colorBox, row, 1) 132 self.allColorBoxCtrls[eachColorBoxInfo['name']] = colorBox 133 134 layout.setColumnStretch(0, 0) 135 layout.setColumnStretch(1, 1) 136 return colorParaGroupBox 137 138 def createRadiusParaGroupBox(self): 139 radiusParaGroupBox = QGroupBox(u"半径设置(1~500)", self) 140 layout = QGridLayout(radiusParaGroupBox) 141 layout.setSpacing(10) 142 143 self.allRadiusCtrls = {} 144 for eachRadiusInfo, row in zip(allRadiusInfo, range(len(allRadiusInfo))): 145 layout.addWidget(QLabel(eachRadiusInfo['label']), row, 0) 146 radiusCtrl = MyRadiusCtrl(name=eachRadiusInfo['name'], initVal=eachRadiusInfo['defaultRadius']) 147 layout.addWidget(radiusCtrl, row, 1) 148 self.allRadiusCtrls[eachRadiusInfo['name']] = radiusCtrl 149 150 layout.setColumnStretch(0, 0) 151 layout.setColumnStretch(1, 1) 152 return radiusParaGroupBox 153 154 class MainWindow(QMainWindow): 155 def __init__(self): 156 super(MainWindow, self).__init__() 157 self.initUI() 158 self.initSlotFunc() 159 self.cnt = 0 160 self.show() 161 162 def initUI(self): 163 self.resize(580, 350) 164 self.setWindowTitle(u'自定义圆形指示灯控件') 165 166 mainSplitter = self.createSplitter(style=Qt.Horizontal, parent=self, width=4) 167 168 self.configWnd = ConfigWnd(mainSplitter) 169 170 rightSplitter = self.createSplitter(style=Qt.Vertical, parent=mainSplitter, width=4) 171 172 rightTopWnd = self.createSubWnd(rightSplitter) 173 rightTopLayout = QVBoxLayout(rightTopWnd) 174 rightTopLayout.setContentsMargins(60, 60, 60, 60) 175 self.ledSingle = MyLed() 176 self.ledSingle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 177 rightTopLayout.addWidget(self.ledSingle) 178 179 rightBottomWnd = self.createSubWnd(rightSplitter) 180 rightBottomLayout = QHBoxLayout(rightBottomWnd) 181 rightBottomLayout.setContentsMargins(10, 10, 10, 10) 182 self.ledGroup = [] 183 for i in range(8): 184 led = MyLed() 185 led.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 186 self.ledGroup.append(led) 187 rightBottomLayout.addWidget(led) 188 189 self.setSplitterStrechFactor(rightSplitter, 3, 0) 190 self.setSplitterStrechFactor(mainSplitter, 0, 1) 191 self.setCentralWidget(mainSplitter) 192 193 def createSplitter(self, style=Qt.Horizontal, parent=None, width=3): 194 splitter = QSplitter(style, parent) 195 splitter.setHandleWidth(width) 196 return splitter 197 198 def setSplitterStrechFactor(self, splitter=None, factor1=1, factor2=1): #设置分割条两部分的比例 199 splitter.setStretchFactor(0, factor1) 200 splitter.setStretchFactor(1, factor2) 201 202 def createSubWnd(self, parent=None): 203 wnd = QFrame(parent) 204 wnd.setFrameStyle(QFrame.Box | QFrame.Sunken) 205 return wnd 206 207 def initSlotFunc(self): 208 self.configWnd.restoreDefaultBtn.clicked.connect(self.slotRestoreDefault) 209 map(lambda x: x.sigColorChanged.connect(self.slotColorChanged), self.configWnd.allColorBoxCtrls.values()) #设定每个颜色控件的槽函数 210 map(lambda x: x.valueChanged.connect(self.slotRadiusChanged), self.configWnd.allRadiusCtrls.values()) #设定每个半径控件的槽函数 211 self.configWnd.animateBtn.clicked.connect(self.slotAnimation) 212 self.timer = QTimer() 213 self.timer.timeout.connect(self.slotTimeout) #动画定时器 214 215 def slotColorChanged(self, color): 216 self.ledSingle.setLedOption(self.sender().name, color) 217 218 def slotRadiusChanged(self, radius): 219 self.ledSingle.setLedOption(self.sender().name, radius) 220 221 def slotRestoreDefault(self): 222 for name, val in zip([boxInfo['name'] for boxInfo in allColorBoxInfo], [boxInfo['defaultColor'] for boxInfo in allColorBoxInfo]): 223 self.configWnd.allColorBoxCtrls[name].setColor(val) 224 225 for name, val in zip([radiusInfo['name'] for radiusInfo in allRadiusInfo], [radiusInfo['defaultRadius'] for radiusInfo in allRadiusInfo]): 226 self.configWnd.allRadiusCtrls[name].setValue(val) 227 228 self.ledSingle.setLedDefaultOption() 229 230 def slotAnimation(self): 231 if self.configWnd.animateBtn.isChecked(): 232 self.cnt = 0 233 self.configWnd.animateBtn.setText(u'停止动画') 234 self.timer.start(300) 235 else: 236 self.configWnd.animateBtn.setText(u'开始动画') 237 self.timer.stop() 238 239 def slotTimeout(self): 240 self.cnt = self.cnt % 256 241 ledBits = QString('%1').arg(self.cnt, 8, 2, fillChar=QChar('0')) #将数值转换为二进制字符串 242 for ledBit, led in zip(ledBits, self.ledGroup): 243 led.setChecked(ledBit=='1') 244 self.cnt += 1 245 246 def main(): 247 app = QApplication(sys.argv) 248 mainWnd = MainWindow() 249 sys.exit(app.exec_()) 250 251 if __name__ == '__main__': 252 main()