├── LICENSE ├── QSS.py ├── README.md ├── demo ├── gifPlayer.gif ├── navigationWidget.gif └── pageWidget.gif ├── gifPlayer.py ├── navigationWidget.py └── pageWidget.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 苗臣亮 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QSS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | # @Time : 2020/2/25 18:43 4 | # @Email : spirit_az@foxmail.com 5 | # @Name : QSS.py 6 | __author__ = 'miaochenliang' 7 | 8 | # import--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # 9 | project_style_sheet = """ 10 | QComboBox { 11 | border: none; 12 | border-radius: 6px; 13 | font: 20px "Microsoft Yahei"; 14 | background-color: rgba(0, 0, 0, 0); 15 | } 16 | 17 | QComboBox::drop-down { 18 | border:none; 19 | } 20 | """ 21 | 22 | project_style_sheet_w = """ 23 | QComboBox { 24 | border: 2px solid #aaa; 25 | border-radius: 6px; 26 | font: 20px "Microsoft Yahei"; 27 | background-color: rgba(0, 0, 0, 0); 28 | } 29 | 30 | QComboBox::drop-down { 31 | border:none; 32 | } 33 | """ 34 | 35 | page_selected = """ 36 | QLabel{ 37 | border: 1px solid #aaa; 38 | border-radius: 4px; 39 | font: 15px "Microsoft Yahei"; 40 | 41 | } 42 | """ 43 | 44 | page_noSelected = """ 45 | QLabel{ 46 | border: none; 47 | font: 12px "Microsoft Yahei"; 48 | } 49 | 50 | QLabel::drop-down { 51 | border:none; 52 | } 53 | 54 | QLabel::hover{ 55 | border: 1px solid #aaa; 56 | border-radius: 4px; 57 | font: 15px "Microsoft Yahei"; 58 | 59 | } 60 | 61 | QLabel::pressed{ 62 | border: none; 63 | font: 12px "Microsoft Yahei"; 64 | 65 | } 66 | 67 | """ 68 | 69 | page_lock = """ 70 | QLabel{ 71 | border: none; 72 | font: 12px "Microsoft Yahei"; 73 | } 74 | """ 75 | 76 | page_lineEdit = """ 77 | QLineEdit{ 78 | background:transparent; 79 | border-width: 0; 80 | font: 12px "Microsoft Yahei"; 81 | border-style:outset} 82 | """ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyQt 2 | 有趣的Qt控件 3 | 4 | # navigationWidget 5 | ![](./demo/navigationWidget.gif) 6 | # pageWidget 7 | ![](./demo/pageWidget.gif) 8 | # gifPlayer 9 | ![](./demo/gifPlayer.gif) -------------------------------------------------------------------------------- /demo/gifPlayer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MChenLiang/PyQt/47e2c833edb8bf3fe0da7282bb7e9e0ea603524c/demo/gifPlayer.gif -------------------------------------------------------------------------------- /demo/navigationWidget.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MChenLiang/PyQt/47e2c833edb8bf3fe0da7282bb7e9e0ea603524c/demo/navigationWidget.gif -------------------------------------------------------------------------------- /demo/pageWidget.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MChenLiang/PyQt/47e2c833edb8bf3fe0da7282bb7e9e0ea603524c/demo/pageWidget.gif -------------------------------------------------------------------------------- /gifPlayer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # Time : 2020/4/9 16:17 4 | # Email : spirit_az@foxmail.com 5 | # File : gifPlayer.py 6 | __author__ = 'ChenLiang.Miao' 7 | # import --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # 8 | from PyQt5 import QtWidgets as QtWidgets 9 | from PyQt5 import QtGui as QtGui 10 | from PyQt5 import QtCore as QtCore 11 | 12 | 13 | # function +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # 14 | 15 | 16 | # +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # 17 | class gifPlayer(QtWidgets.QWidget): 18 | # 拖拽判定 19 | m_pressed = False 20 | 21 | def __init__(self, filename, parent=None): 22 | super(gifPlayer, self).__init__(parent=parent) 23 | 24 | # 自动填充 25 | self.setAutoFillBackground(True) 26 | # 可推拽 27 | self.setMouseTracking(True) 28 | # 无边框 29 | self.setWindowFlag(QtCore.Qt.FramelessWindowHint) 30 | 31 | self.movie_screen = QtWidgets.QLabel() 32 | # Make label fit the gif 33 | self.movie_screen.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 34 | self.movie_screen.setAlignment(QtCore.Qt.AlignCenter) 35 | 36 | # Create the layout 37 | main_layout = QtWidgets.QVBoxLayout() 38 | main_layout.setContentsMargins(0, 0, 0, 0) 39 | main_layout.addWidget(self.movie_screen) 40 | 41 | self.setLayout(main_layout) 42 | 43 | # 加载动图 44 | self.movie = QtGui.QMovie(filename, QtCore.QByteArray(), self) 45 | # 设置尺寸 46 | self.resize(self.movie.scaledSize()) 47 | # 缓存: 无限循环播放 48 | self.movie.setCacheMode(QtGui.QMovie.CacheAll) 49 | # 播放速度为1 50 | self.movie.setSpeed(100) 51 | # 加载到UI中 52 | self.movie_screen.setMovie(self.movie) 53 | # 开始播放 54 | self.movie.start() 55 | 56 | # 设置窗口可被拖动+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--# 57 | def mousePressEvent(self, event): 58 | if event.buttons() == QtCore.Qt.LeftButton: 59 | self.c_pos = event.globalPos() - self.pos() 60 | self.m_pressed = True 61 | 62 | def mouseMoveEvent(self, event): 63 | if event.buttons() == QtCore.Qt.LeftButton: 64 | if self.m_pressed: 65 | self.move(event.globalPos() - self.c_pos) 66 | event.accept() 67 | 68 | def mouseReleaseEvent(self, event): 69 | self.m_pressed = False 70 | 71 | def keyPressEvent(self, QKeyEvent): 72 | # super(gifPlayer, self).keyPressEvent() 73 | modifiers = QtWidgets.QApplication.keyboardModifiers() 74 | if modifiers & QtCore.Qt.ControlModifier | modifiers & QtCore.Qt.ShiftModifier: 75 | print('active') 76 | 77 | def keyReleaseEvent(self, QKeyEvent): 78 | print('active close') 79 | 80 | 81 | if __name__ == '__main__': 82 | app = QtWidgets.QApplication([]) 83 | ui = gifPlayer('demo/gifPlayer.gif') 84 | ui.show() 85 | app.exec_() 86 | -------------------------------------------------------------------------------- /navigationWidget.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | # @Time : 2020/2/27 11:56 4 | # @Email : spirit_az@foxmail.com 5 | # @Name : navigationWidget.py 6 | __author__ = 'miaochenliang' 7 | 8 | # import--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # 9 | # |asset|shot 10 | # —————————————————————————————————————————————————— 11 | # (typeName|shot) | (assetName|eps_name) | task_name | pipeline | 12 | 13 | # ---------------------------------------------------------------------------------------------------- 14 | from PyQt5 import QtCore 15 | from PyQt5 import QtGui 16 | from PyQt5 import QtWidgets 17 | import math 18 | 19 | # ---------------------------------------------------------------------------------------------------- 20 | class _mode(object): 21 | # -True:横向, False:纵向 22 | __isH = True 23 | # - {0:居中, 1:(左|上), 2:(右|下)} 24 | __match = 0 25 | # dis 26 | __dis = 80 27 | 28 | # 控件滑动后的数值记录 29 | __offset = 0 30 | 31 | # 颜色 32 | __selColor = QtCore.Qt.white 33 | __unSelColor = QtCore.Qt.gray 34 | 35 | @property 36 | def isH(self): 37 | return self.__isH 38 | 39 | @isH.setter 40 | def isH(self, val): 41 | self.__isH = val 42 | 43 | @property 44 | def match(self): 45 | return self.__match 46 | 47 | @match.setter 48 | def match(self, val): 49 | self.__match = val 50 | 51 | @property 52 | def dis(self): 53 | return self.__dis 54 | 55 | @dis.setter 56 | def dis(self, val): 57 | self.__dis = val 58 | 59 | @property 60 | def offset(self): 61 | return self.__offset 62 | 63 | @offset.setter 64 | def offset(self, val): 65 | self.__offset = val 66 | 67 | @property 68 | def selColor(self): 69 | return self.__selColor 70 | 71 | @selColor.setter 72 | def selColor(self, QColor): 73 | self.__selColor = QColor 74 | 75 | @selColor.deleter 76 | def selColor(self): 77 | self.__selColor = QtCore.Qt.white 78 | 79 | @property 80 | def unSelColor(self): 81 | return self.__unSelColor 82 | 83 | @unSelColor.setter 84 | def unSelColor(self, QColor): 85 | self.__unSelColor = QColor 86 | 87 | @unSelColor.deleter 88 | def unSelColor(self): 89 | self.__unSelColor = QtCore.Qt.gray 90 | 91 | 92 | class navigationTitle(QtWidgets.QWidget, _mode): 93 | currentItemChanged = QtCore.pyqtSignal([int, str]) 94 | 95 | def __init__(self, isH=True, match=0, dis=80, parent=None): 96 | super(navigationTitle, self).__init__(parent) 97 | self.setMouseTracking(True) 98 | # 背景颜色 99 | self.backgroundColor = QtGui.QColor("#ffffff") 100 | self.currentIndex = 0 101 | # item 个数 102 | self.listItems = [] 103 | # 当前选择的项索引 104 | self.currentIndex = 0 105 | # 当前光标所在位置的项索引 106 | self.cursorIndex = -1 107 | # 鼠标当前位置,用于滑动 108 | self.c_pos = QtCore.QPoint() 109 | # 滑动数值 110 | self.slider = 0 111 | 112 | # 横向还是纵向 113 | self.isH = isH 114 | # 布局规则: 左|上, 中间, 右|下 115 | self.match = match 116 | # 每一个部件的占位长度 117 | self.dis = dis 118 | # 标准初始位置 119 | self.cOffset = 0 120 | 121 | def offsetChangedIndex(self, val): 122 | """ 123 | 124 | :param val: 1: 右|下, 2: 左上 125 | :return: 126 | """ 127 | currentIndex = self.currentIndex 128 | minVal = 0 129 | maxVal = len(self.listItems) - 1 130 | # 得到当前index 131 | currentIndex += val 132 | if currentIndex < minVal: 133 | currentIndex = minVal 134 | elif currentIndex > maxVal: 135 | currentIndex = maxVal 136 | # 如果没有变动,就不执行 137 | if currentIndex == self.currentIndex: 138 | return 139 | # 执行切换 140 | self.setCurrentIndex(currentIndex) 141 | 142 | def setCurrentIndex(self, idx): 143 | """ 144 | 切换当前所选的界面 145 | :param idx: 146 | :return: 147 | """ 148 | self.currentIndex = idx 149 | self.currentItemChanged.emit(idx, self.listItems[idx]) 150 | self.update() 151 | 152 | def addItem(self, item): 153 | """ 154 | 添加一个分页 155 | :param item: 分页的名称 156 | :return: 157 | """ 158 | self.listItems.append(item) 159 | self.update() 160 | 161 | def setItems(self, items): 162 | """ 163 | 设置所有的分页 164 | :param items: 当前所有分页的名称 165 | :return: 166 | """ 167 | self.listItems = items 168 | self.update() 169 | 170 | def setOffset(self, val): 171 | """ 172 | item的滑动效果 173 | :param val: 174 | :return: 175 | """ 176 | self.offset = val 177 | self.update() 178 | 179 | def __getPos(self): 180 | """ 181 | 得到每一个item的pos 182 | :return: [width, height] 183 | """ 184 | isH, match = self.isH, self.match 185 | itemLength = len(self.listItems) 186 | itemsDis = self.dis * itemLength 187 | 188 | h, w = self.height(), self.width() 189 | 190 | # 得到坐标位置 191 | tDis = w if isH else h 192 | if match == 0: 193 | self.cOffset = (tDis - itemsDis) * 0.5 194 | elif match == 1: 195 | self.cOffset = 0 196 | else: 197 | self.cOffset = tDis - itemsDis 198 | 199 | self.cOffset += self.offset 200 | self.cOffset += self.slider 201 | 202 | Xs = [[self.cOffset + i * self.dis, 0] for i in range(itemLength)] 203 | if not isH: 204 | list(map(lambda x: x.reverse(), Xs)) 205 | 206 | return Xs 207 | 208 | def setBackGroundColor(self, QColor): 209 | self.backgroundColor = QColor 210 | self.update() 211 | 212 | def paintEvent(self, evt): 213 | """ 214 | 绘制背景 215 | :param evt: 216 | :return: 217 | """ 218 | painter = QtGui.QPainter(self) 219 | painter.setRenderHint(QtGui.QPainter.Antialiasing) 220 | 221 | # 画背景色 222 | painter.setPen(QtCore.Qt.NoPen) 223 | painter.setBrush(self.backgroundColor) 224 | painter.drawRect(self.rect()) 225 | 226 | # 得到item的位置坐标 227 | pos = self.__getPos() 228 | 229 | # 画子项 230 | 231 | pSize = (self.dis, self.height()) if self.isH else (self.width(), self.dis) 232 | 233 | for (i, (x, y)) in enumerate(pos): 234 | itemPath = QtGui.QPainterPath() 235 | if self.isH: 236 | rectX, rectY = pSize[0] - 1, pSize[1] / 10 - 1 237 | rectPx, rectPy = x, y + pSize[1] * 0.9 + 1 238 | else: 239 | rectX, rectY = pSize[0] / 30 - 1, pSize[1] - 1 240 | rectPx, rectPy = x, y 241 | itemPath.addRect(QtCore.QRectF(rectPx, rectPy, rectX, rectY)) 242 | 243 | # 设置绘制字体 244 | font = QtGui.QFont() 245 | font.setFamily('Microsoft Yahei') 246 | font.setPointSize(12) 247 | 248 | if i == self.currentIndex and i == self.cursorIndex: 249 | font.setPointSize(16) 250 | elif i == self.currentIndex or i == self.cursorIndex: 251 | font.setPointSize(15) 252 | 253 | painter.setFont(font) 254 | 255 | # 开始绘制 256 | if i == self.currentIndex: 257 | painter.setPen(self.selColor) 258 | painter.fillPath(itemPath, self.selColor) 259 | painter.drawText(QtCore.QRect(x, y, pSize[0], pSize[1]), 260 | QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter, self.listItems[i]) 261 | elif i == self.cursorIndex: 262 | painter.setPen(self.selColor) 263 | painter.drawText(QtCore.QRect(x, y, pSize[0], pSize[1]), 264 | QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter, self.listItems[i]) 265 | 266 | else: 267 | painter.setPen(self.unSelColor) 268 | painter.drawText(QtCore.QRect(x, y, pSize[0], pSize[1]), 269 | QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter, self.listItems[i]) 270 | 271 | painter.end() 272 | 273 | def mouseMoveEvent(self, event): 274 | """ 275 | 中间为滑动效果,其他为鼠标经过效果 276 | :param event: 277 | :return: 278 | """ 279 | if event.buttons() == QtCore.Qt.MiddleButton: 280 | if self.m_pressed: 281 | movePos = event.globalPos() - self.c_pos 282 | self.slider = movePos.x() if self.isH else movePos.y() 283 | event.accept() 284 | self.update() 285 | else: 286 | evtL = event.x() if self.isH else event.y() 287 | evtL -= self.cOffset 288 | idx = int(evtL / self.dis) 289 | if idx >= len(self.listItems): 290 | idx = -1 291 | if idx < len(self.listItems) and idx != self.cursorIndex: 292 | self.update() 293 | self.cursorIndex = idx 294 | 295 | def mousePressEvent(self, QMouseEvent): 296 | """ 297 | 中间为滑动效果,其他为选中效果 298 | :param QMouseEvent: 299 | :return: 300 | """ 301 | if QMouseEvent.buttons() == QtCore.Qt.MiddleButton: 302 | self.c_pos = QMouseEvent.globalPos() # - self.pos() # type: QtCore.QPoint 303 | self.m_pressed = True 304 | elif QMouseEvent.button() == QtCore.Qt.RightButton: 305 | # 恢复初始化位置 306 | self.setOffset(0) 307 | else: 308 | evtL = QMouseEvent.x() if self.isH else QMouseEvent.y() 309 | evtL -= self.cOffset 310 | idx = int(evtL / self.dis) 311 | if 0 <= idx < len(self.listItems): 312 | self.currentIndex = idx 313 | self.currentItemChanged.emit(idx, self.listItems[idx]) 314 | self.update() 315 | 316 | def mouseReleaseEvent(self, QMouseEvent): 317 | self.m_pressed = False 318 | self.offset += self.slider 319 | self.slider = 0 320 | 321 | def leaveEvent(self, QMouseEvent): 322 | self.cursorIndex = -1 323 | self.update() 324 | 325 | 326 | class navigationWidget(QtWidgets.QWidget): 327 | def __init__(self, isH=True, match=0, dis=80, fixSize=40, parent=None): 328 | """ 329 | # 竖向 330 | uiH = navigationWidget(isH=False, match=0, dis=40, fixSize=120) 331 | # 横向 332 | uiV = navigationWidget(isH=True, match=0, dis=120, fixSize=40) 333 | 334 | :param isH: title为横向还是竖向 335 | :param match: 对齐关系, 左上|居中|右下 336 | :param dis: item的占位 337 | :param fixSize: title的占位 338 | :param parent:父物体控件 339 | """ 340 | super(navigationWidget, self).__init__(parent=parent) 341 | if isH: 342 | lay = QtWidgets.QVBoxLayout(self) 343 | else: 344 | lay = QtWidgets.QHBoxLayout(self) 345 | lay.setSpacing(0) 346 | # lay.setContentsMargins(0, 0, 0, 0) 347 | self.navigationTitle = navigationTitle(isH, match, dis, self) 348 | if isH: 349 | self.navigationTitle.setFixedHeight(fixSize) 350 | else: 351 | self.navigationTitle.setFixedWidth(fixSize) 352 | 353 | lay.addWidget(self.navigationTitle) 354 | 355 | self.centralWidget = QtWidgets.QWidget(self) 356 | lay.addWidget(self.centralWidget) 357 | 358 | self.currentItemChanged = self.navigationTitle.currentItemChanged 359 | 360 | def setItems(self, items): 361 | items = ['%s' % item for item in items] 362 | self.navigationTitle.setItems(items) 363 | 364 | def setSelectColor(self, QColor): 365 | self.navigationTitle.selColor = QColor 366 | 367 | def setUnSelectColor(self, QColor): 368 | self.navigationTitle.unSelColor = QColor 369 | 370 | def setBackGroundColor(self, QColor): 371 | self.navigationTitle.setBackGroundColor(QColor) 372 | 373 | def _tmpInitUI(self): 374 | self.label = QtWidgets.QLabel('当前是第0页', self.centralWidget) 375 | self.label.setAlignment(QtCore.Qt.AlignCenter) 376 | lay = QtWidgets.QHBoxLayout() 377 | self.centralWidget.setLayout(lay) 378 | lay.addWidget(self.label) 379 | self.currentItemChanged.connect(self._tmpClicked) 380 | 381 | def _tmpClicked(self, int, str): 382 | self.label.setText('当前是第{}页'.format(int)) 383 | 384 | 385 | if __name__ == '__main__': 386 | app = QtWidgets.QApplication([]) 387 | # 页码0 388 | titleList = ['页码%d' % i for i in range(50)] 389 | # # 竖向 390 | uiH = navigationWidget(isH=False, match=1, dis=40, fixSize=120) 391 | # 设置分页数 392 | uiH.setItems(titleList) 393 | # 设置尺寸 394 | uiH.resize(600, 400) 395 | # 设置背景颜色 396 | uiH.setBackGroundColor(QtGui.QColor("#E4E4E4")) 397 | # 设置颜色 398 | uiH.setSelectColor(QtGui.QColor('#2CA7F8')) 399 | uiH.setUnSelectColor(QtGui.QColor('#202020')) 400 | # 显示窗口 401 | uiH._tmpInitUI() 402 | uiH.show() 403 | # 横向 404 | uiV = navigationWidget(isH=True, match=1, dis=120, fixSize=40) 405 | # 设置分页数 406 | uiV.setItems(titleList) 407 | # 设置尺寸 408 | uiV.resize(600, 400) 409 | # 设置背景颜色 410 | uiV.setBackGroundColor(QtGui.QColor("#E4E4E4")) 411 | # 设置颜色 412 | uiV.setSelectColor(QtGui.QColor('#2CA7F8')) 413 | uiV.setUnSelectColor(QtGui.QColor('#202020')) 414 | # 显示窗口 415 | uiV._tmpInitUI() 416 | uiV.show() 417 | app.exec_() 418 | -------------------------------------------------------------------------------- /pageWidget.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:UTF-8 -*- 3 | # @Time : 2020/3/2 16:25 4 | # @Email : spirit_az@foxmail.com 5 | # @Name : pageWidget.py 6 | __author__ = 'miaochenliang' 7 | 8 | # import--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ # 9 | from functools import partial 10 | from PyQt5 import QtCore 11 | from PyQt5 import QtGui 12 | from PyQt5 import QtWidgets 13 | 14 | try: 15 | from .. import QSS 16 | except: 17 | class QSS: 18 | page_selected = """ 19 | QLabel{ 20 | border: 1px solid #aaa; 21 | border-radius: 4px; 22 | font: 15px "Microsoft Yahei"; 23 | 24 | } 25 | """ 26 | 27 | page_noSelected = """ 28 | QLabel{ 29 | border: none; 30 | font: 12px "Microsoft Yahei"; 31 | } 32 | 33 | QLabel::drop-down { 34 | border:none; 35 | } 36 | 37 | QLabel::hover{ 38 | border: 1px solid #aaa; 39 | border-radius: 4px; 40 | font: 15px "Microsoft Yahei"; 41 | 42 | } 43 | 44 | QLabel::pressed{ 45 | border: none; 46 | font: 12px "Microsoft Yahei"; 47 | 48 | } 49 | 50 | """ 51 | 52 | page_lock = """ 53 | QLabel{ 54 | border: none; 55 | font: 12px "Microsoft Yahei"; 56 | } 57 | """ 58 | 59 | page_lineEdit = """ 60 | QLineEdit{ 61 | background:transparent; 62 | border-width: 0; 63 | font: 12px "Microsoft Yahei"; 64 | border-style:outset} 65 | """ 66 | 67 | 68 | # ---------------------------------------------------------------------------------------------------- 69 | # 页码数 70 | class pageLineEditor(QtWidgets.QLineEdit): 71 | textModified = QtCore.pyqtSignal(str) 72 | 73 | def __init__(self, index, parent=None): 74 | self._before = str(index) 75 | super(pageLineEditor, self).__init__(self._before, parent) 76 | self.setFrame(False) 77 | self.setAlignment(QtCore.Qt.AlignCenter) 78 | self.returnPressed.connect(self.checkText) 79 | 80 | self.setFixedWidth(25) 81 | 82 | self.setStyleSheet(QSS.page_lineEdit) 83 | 84 | def setMax(self, index): 85 | self.validator = QtGui.QIntValidator(1, index, self) 86 | self.setValidator(self.validator) 87 | 88 | def setText(self, p_str): 89 | self._before = str(p_str) 90 | super(pageLineEditor, self).setText(self._before) 91 | 92 | def focusInEvent(self, event): 93 | if event.reason() != QtCore.Qt.PopupFocusReason: 94 | self._before = self.text() 95 | super(pageLineEditor, self).focusInEvent(event) 96 | 97 | def focusOutEvent(self, event): 98 | if event.reason() != QtCore.Qt.PopupFocusReason: 99 | self.checkText() 100 | super(pageLineEditor, self).focusOutEvent(event) 101 | 102 | def checkText(self): 103 | if self._before != self.text(): 104 | self._before = self.text() 105 | self.textModified.emit(self._before) 106 | 107 | 108 | # 页码条里的label 109 | class pageLabel(QtWidgets.QLabel): 110 | clicked = QtCore.pyqtSignal(str) 111 | 112 | __isSelected = False 113 | 114 | @property 115 | def isSelected(self): 116 | return self.__isSelected 117 | 118 | @isSelected.setter 119 | def isSelected(self, val): 120 | self.__isSelected = val 121 | 122 | @isSelected.deleter 123 | def isSelected(self): 124 | self.__isSelected = False 125 | 126 | def __init__(self, p_str, *args): 127 | p_str = str(p_str) 128 | super(pageLabel, self).__init__(p_str, *args) 129 | self.isLock = False 130 | self.setStyleSheet(QSS.page_noSelected) 131 | self.setAlignment(QtCore.Qt.AlignCenter) 132 | self.setFixedSize(25, 25) 133 | 134 | def mouseReleaseEvent(self, QMouseEvent): 135 | self.clicked.emit(self.text()) 136 | 137 | def setLock(self, val): 138 | if val: 139 | self.setText('...') 140 | self.isLock = True 141 | else: 142 | self.isLock = False 143 | self.setSelected(self.isSelected) 144 | 145 | def setSelected(self, val): 146 | self.isSelected = False 147 | if self.isLock: 148 | self.setStyleSheet(QSS.page_lock) 149 | elif val: 150 | self.setStyleSheet(QSS.page_selected) 151 | self.isSelected = True 152 | else: 153 | self.setStyleSheet(QSS.page_noSelected) 154 | 155 | def setText(self, p_str): 156 | super(pageLabel, self).setText(str(p_str)) 157 | 158 | 159 | # 页码条 160 | class navigationpages(QtWidgets.QWidget): 161 | valueChanged = QtCore.pyqtSignal(int, int) 162 | itemChanged = QtCore.pyqtSignal(int) 163 | _pageMax = 10 164 | _totalNumber = 500 165 | 166 | @property 167 | def totalNumber(self): 168 | return self._totalNumber 169 | 170 | @totalNumber.setter 171 | def totalNumber(self, val): 172 | self._totalNumber = val 173 | page = self._totalNumber / self.pageItemNumber 174 | offset = self._totalNumber % self.pageItemNumber 175 | if offset: 176 | page += 1 177 | # 重新设置page 178 | self.pageMax = page 179 | self.__setSelected(self.label01, 1) 180 | # 发出修改信号 181 | self.current_valueChanged_connect(1) 182 | 183 | @totalNumber.deleter 184 | def totalNumber(self): 185 | self._totalNumber = 500 186 | 187 | @property 188 | def pageMax(self): 189 | return self._pageMax 190 | 191 | @pageMax.setter 192 | def pageMax(self, val): 193 | self._pageMax = val 194 | self.lineEditor_current.setMax(val) 195 | # 把所有的页码都隐藏 196 | for page in self.numberPage: 197 | page.setHidden(True) 198 | 199 | # 把范围内的全部显示出来 200 | maxNum = self.pageMax if self.pageMax < 8 else 8 201 | for i in range(maxNum): 202 | page = self.numberPage[i] 203 | page.setHidden(False) 204 | 205 | # 初始化页数 206 | self.maxNumber.setText(str(val)) 207 | # 设置最后一页为最大值 208 | self.label08.setText(self.pageMax) 209 | 210 | if self.pageMax in [7, 8]: 211 | self.label07.setText(7) 212 | self.label07.setLock(False) 213 | self.__setSelected(self.label01, 1) 214 | 215 | @pageMax.deleter 216 | def pageMax(self): 217 | self._pageMax = 20 218 | 219 | def __init__(self, parent=None): 220 | super(navigationpages, self).__init__(parent) 221 | # 当前每页的可选条目 222 | self.pageItemN = [20, 50, 100] 223 | # 当前每页条目 224 | self.pageItemNumber = self.pageItemN[1] 225 | # 当前页码 226 | self.pageNumber = 0 227 | self.initUI() 228 | self.btClicked() 229 | 230 | def initUI(self): 231 | lay = QtWidgets.QHBoxLayout(self) 232 | lay.setContentsMargins(0, 0, 0, 0) 233 | # 每页多少个 234 | self.itemNumber_001 = pageLabel(self.pageItemN[0]) 235 | self.itemNumber_002 = pageLabel(self.pageItemN[1]) 236 | self.itemNumber_002.setSelected(True) 237 | self.itemNumber_003 = pageLabel(self.pageItemN[2]) 238 | 239 | self.itemNumber = [self.itemNumber_001, self.itemNumber_002, self.itemNumber_003] 240 | for l in self.itemNumber: 241 | l.setFixedWidth(40) 242 | lay.addWidget(l) 243 | # 加个弹簧顶起来 244 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 245 | lay.addItem(spacerItem) 246 | # 页码 247 | label = QtWidgets.QLabel('Page') 248 | label.setAlignment(QtCore.Qt.AlignCenter) 249 | lay.addWidget(label) 250 | self.lineEditor_current = pageLineEditor(0, self) 251 | self.lineEditor_current.setMax(self.pageMax) 252 | lay.addWidget(self.lineEditor_current) 253 | label = QtWidgets.QLabel('of') 254 | label.setAlignment(QtCore.Qt.AlignCenter) 255 | lay.addWidget(label) 256 | self.maxNumber = QtWidgets.QLabel('%s' % self.pageMax) 257 | self.maxNumber.setAlignment(QtCore.Qt.AlignCenter) 258 | lay.addWidget(self.maxNumber) 259 | 260 | # 页码 261 | self.upPage = pageLabel('<<') 262 | self.label01 = pageLabel('1') 263 | self.label02 = pageLabel('2') 264 | self.label03 = pageLabel('3') 265 | self.label04 = pageLabel('4') 266 | self.label05 = pageLabel('5') 267 | self.label06 = pageLabel('6') 268 | self.label07 = pageLabel('...') 269 | self.label08 = pageLabel('10') 270 | self.dnPage = pageLabel('>>') 271 | 272 | self.numberPage = [self.label01, self.label02, self.label03, self.label04, 273 | self.label05, self.label06, self.label07, self.label08] 274 | 275 | self.numberUpPage = [self.label01, self.label02, self.label03, self.label04, 276 | self.label05, self.label06] 277 | 278 | self.numberDnPage = [self.label03, self.label04, 279 | self.label05, self.label06, self.label07, self.label08] 280 | 281 | self.numberMdPage = [self.label03, self.label04, self.label05, self.label06] 282 | 283 | lay.addWidget(self.upPage) 284 | for l in self.numberPage: 285 | lay.addWidget(l) 286 | lay.addWidget(self.dnPage) 287 | 288 | self.setSelected(self.label01, 1) 289 | 290 | def btClicked(self): 291 | # 页码数 292 | for item in self.itemNumber: 293 | item.clicked.connect(self.itemNumber_clicked_connect) 294 | 295 | # 页码条 296 | for l in self.numberPage: 297 | l.clicked.connect(partial(self.setSelected, l)) 298 | 299 | self.upPage.clicked.connect(partial(self.udPage, -1)) 300 | self.dnPage.clicked.connect(partial(self.udPage, 1)) 301 | 302 | # 页码条跳转到第几页 303 | self.lineEditor_current.textModified.connect(self.textModified_connect) 304 | 305 | @QtCore.pyqtSlot(str) 306 | def itemNumber_clicked_connect(self, val): 307 | val = int(val) 308 | if val == self.pageItemNumber: 309 | return 310 | 311 | if val not in self.pageItemN: 312 | return 313 | 314 | currentNumber = self.pageItemNumber 315 | # 设置当前选择的pageItem 316 | for each in self.itemNumber: 317 | each.setSelected(False) 318 | 319 | for each in self.itemNumber: 320 | if each.text() == str(val): 321 | self.pageItemNumber = val 322 | each.setSelected(True) 323 | break 324 | 325 | # 重新设置page 326 | pageMax = self.totalNumber / val 327 | if self.totalNumber % val: 328 | pageMax += 1 329 | self.pageMax = pageMax 330 | self.__setSelected(self.label01, 1) 331 | # 发出修改信号 332 | self.itemChanged.emit(val) 333 | print(u'每页可以加载由{}改为{}条数据'.format(currentNumber, val)) 334 | self.current_valueChanged_connect(1) 335 | 336 | def textModified_connect(self, index): 337 | """ 338 | 编辑窗口输出 339 | :param index: 340 | :return: 341 | """ 342 | index = int(index) 343 | if self.pageMax < 9: 344 | self.__setSelected(self.numberPage[index - 1], index) 345 | self.current_valueChanged_connect(index) 346 | return 347 | 348 | if index >= 6: 349 | number = index if index < self.pageMax - 3 else self.pageMax - 3 350 | self.__setSelected(self.label05, number) 351 | 352 | if index <= self.pageMax - 5: 353 | number = index if index > 4 else 4 354 | self.__setSelected(self.label04, number) 355 | 356 | for each in self.numberPage: 357 | if each.text() == str(index): 358 | self.__setSelected(each, index) 359 | self.current_valueChanged_connect(index) 360 | return 361 | 362 | @QtCore.pyqtSlot(int) 363 | def current_valueChanged_connect(self, index): 364 | """ 365 | 366 | :param index: 得到当前页码,并发射信号 367 | :return: 368 | """ 369 | self.pageNumber = index 370 | print(u'第{}页,可以加载{}条数据'.format(index, self.pageItemNumber)) 371 | self.valueChanged.emit(index, self.pageItemNumber) 372 | 373 | def udPage(self, val): 374 | """ 375 | 翻页 376 | :param val: 1:向后一页,-1向前一页 377 | :return: 378 | """ 379 | currentSel = None 380 | currentIndex = -1 381 | for (i, each) in enumerate(self.numberPage): 382 | if each.isSelected: 383 | currentSel = each 384 | currentIndex = i 385 | break 386 | if currentSel == self.label01 and val == -1: 387 | return 388 | if currentSel == self.label08 and val == 1: 389 | return 390 | if int(currentSel.text()) == self.pageMax and val == 1: 391 | return 392 | 393 | currentIndex += val 394 | nextSel = self.numberPage[currentIndex] 395 | self.setSelected(nextSel, nextSel.text()) 396 | pass 397 | 398 | def setSelected(self, sender, index): 399 | """ 400 | 401 | :param sender: 页码控件 402 | :param index: 第几页 403 | :return: 404 | """ 405 | 406 | # 如果选择到了 ... 的控件,不执行 407 | if sender.isLock: 408 | return 409 | 410 | index = int(index) 411 | # 如果是当前页的话,跳过 412 | if index == self.pageNumber: 413 | return 414 | 415 | if sender == self.label01: 416 | self.__setSelected(sender, 1) 417 | # 触发信号发射出去 418 | self.lineEditor_current.setText(1) 419 | self.current_valueChanged_connect(1) 420 | return 421 | 422 | # 触发信号发射出去 423 | self.lineEditor_current.setText(index) 424 | self.current_valueChanged_connect(index) 425 | if sender == self.label03: 426 | if index > 3: 427 | self.__setSelected(self.label04, index) 428 | return 429 | if sender == self.label06: 430 | if index < self.pageMax - 2: 431 | self.__setSelected(self.label05, index) 432 | return 433 | 434 | self.__setSelected(sender, index) 435 | 436 | def __setSelected(self, sender, index): 437 | """ 438 | 页码变更后的窗口修改 439 | :param sender: 440 | :param index: 441 | :return: 442 | """ 443 | # 把所有选择的都关掉 444 | for each in self.numberPage: 445 | each.setSelected(0) 446 | # 设置选择的label 447 | sender.setSelected(1) 448 | sender.setText(index) 449 | # 变小 450 | if sender == self.label04: 451 | if index < self.pageMax - 4: 452 | self.label07.setLock(True) 453 | if index <= 4: 454 | self.label02.setLock(False) 455 | for (i, label) in enumerate(self.numberUpPage): 456 | label.setText(index - 3 + i) 457 | else: 458 | self.label02.setLock(True) 459 | for (i, label) in enumerate(self.numberMdPage): 460 | label.setText(index - 1 + i) 461 | return 462 | 463 | # 变大 464 | if sender == self.label05: 465 | if index > 5: 466 | self.label02.setLock(True) 467 | if index < self.pageMax - 3: 468 | self.label07.setLock(True) 469 | for (i, label) in enumerate(self.numberMdPage): 470 | label.setText(index - 2 + i) 471 | elif index == self.pageMax: 472 | for (i, label) in enumerate(self.numberDnPage): 473 | label.setText(self.pageMax - 2 + i) 474 | else: 475 | self.label07.setLock(False) 476 | pageMax = self.pageMax if self.pageMax > 8 else 8 477 | for (i, label) in enumerate(self.numberDnPage): 478 | label.setText(pageMax - 5 + i) 479 | 480 | return 481 | 482 | # 最开头 483 | if sender == self.label01: 484 | if self.pageMax > 8: 485 | self.label07.setLock(True) 486 | self.label02.setLock(False) 487 | for (i, label) in enumerate(self.numberUpPage): 488 | label.setText(index + i) 489 | return 490 | 491 | if sender == self.label08: 492 | if index > 8: 493 | self.label02.setLock(True) 494 | self.label07.setLock(False) 495 | for (i, label) in enumerate(self.numberDnPage): 496 | label.setText(self.pageMax - 5 + i) 497 | return 498 | 499 | 500 | if __name__ == '__main__': 501 | app = QtWidgets.QApplication([]) 502 | 503 | ui = navigationpages() 504 | ui.show() 505 | ui.totalNumber = 350 506 | # ui.setPageMax(10) 507 | app.exec_() 508 | --------------------------------------------------------------------------------