├── B站直播礼物答谢机.py ├── GIFWidget.py ├── README.md ├── bgm └── 撒钱.mp3 ├── config.json ├── gif ├── 151b09b4713cb579dc5e41257efdad2afb968331.gif └── 清良.gif ├── option.py ├── remote.py └── utils └── qdark.qss /B站直播礼物答谢机.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import shutil 5 | import json 6 | import codecs 7 | from PySide2.QtWidgets import * 8 | from PySide2.QtGui import * 9 | from PySide2.QtCore import * 10 | from PySide2.QtMultimedia import QMediaPlayer 11 | from remote import remoteThread 12 | from GIFWidget import GIFWidget 13 | from option import OptionWidget 14 | 15 | 16 | class Slider(QSlider): 17 | pointClicked = Signal(QPoint) 18 | 19 | def __init__(self): 20 | super(Slider, self).__init__() 21 | self.setOrientation(Qt.Horizontal) 22 | 23 | def mousePressEvent(self, event): 24 | self.pointClicked.emit(event.pos()) 25 | 26 | def mouseMoveEvent(self, event): 27 | self.pointClicked.emit(event.pos()) 28 | 29 | 30 | class previewLabel(QLabel): 31 | click = Signal() 32 | 33 | def __init__(self, text='', parent=None): 34 | super().__init__(parent) 35 | self.setText(text) 36 | self.setAlignment(Qt.AlignCenter) 37 | 38 | def mousePressEvent(self, QEvent): 39 | self.click.emit() 40 | 41 | 42 | class MainWindow(QMainWindow): 43 | def __init__(self): 44 | super().__init__() 45 | self.setFixedWidth(540) 46 | self.sound = QMediaPlayer() 47 | self.executeToken = False 48 | self.setAcceptDrops(True) 49 | self.installEventFilter(self) 50 | self.presetIndex = '0' 51 | if os.path.exists('utils/config.json'): 52 | with codecs.open('utils/config.json', 'r', 'utf_8_sig') as config: 53 | config = config.read() 54 | self.config = json.loads(config) 55 | else: 56 | self.config = {str(x): {'gift': '请输入礼物名字 多个礼物间用空格隔开', 'words': '感谢 {用户} 投喂的{数量}个{礼物}~', 57 | 'bgm_path': '', 'volume': 100, 'gif_path': '', 'gif_scale': 50, 58 | 'second': 4, 'font_color': '#3c9dca', 'out_color': '#1c54a7', 'out_size': 15, 59 | 'font_name': '微软雅黑', 'font_size': 36, 'font_bold': False, 'font_italic': False 60 | } for x in range(10)} 61 | self.config['0']['gift'] = ('辣条 小心心 “棋”开得胜 盛典门票 盛典之杯 吃瓜 冰阔落 给大佬递茶 打榜 盛典小电视 ' 62 | 'B坷垃 天空之翼 摩天大楼 礼花 凉了 比心 喵娘 节奏风暴 疯狂打call 泡泡机 爱之魔力 ' 63 | '摩天轮 转运锦鲤 冲浪 亿圆') 64 | self.config['9']['words'] = '恭迎 {用户} 上舰~~~' 65 | self.config['room_url'] = '123456' 66 | self.config['background_color'] = '#00d600' 67 | self.config['opacity'] = False 68 | self.config['top'] = False 69 | self.gift = self.config['0']['gift'] 70 | self.second = self.config['0']['second'] 71 | self.color = self.config['0']['font_color'] 72 | self.outColor = self.config['0']['out_color'] 73 | self.outSize = self.config['0']['out_size'] 74 | self.gifScale = self.config['0']['gif_scale'] 75 | self.gifSize = None 76 | self.volume = self.config['0']['volume'] 77 | self.oldBGM = self.config['0']['bgm_path'] 78 | self.movieList = [] 79 | self.gifSizeList = [] 80 | for index in [str(i) for i in range(10)]: 81 | movie = QMovie(self.config[index]['gif_path']) 82 | gifSize = QPixmap(self.config[index]['gif_path']).size() 83 | if gifSize: 84 | movie.setScaledSize(gifSize * self.config[index]['gif_scale'] / 50) 85 | self.movieList.append(movie) 86 | self.gifSizeList.append(gifSize) 87 | self.option = OptionWidget(self.config['background_color'], self.config['opacity'], self.config['top']) 88 | self.option.color.connect(self.selectBackgroundColor) 89 | self.option.opacity.connect(self.setopacity) 90 | self.option.top.connect(self.setTop) 91 | 92 | self.GIFWidget = GIFWidget(self.movieList[0], self.config['opacity'], 93 | self.config['top'], self.second * 60, self.color, self.outColor) 94 | self.GIFWidget.showText.w = self.outSize / 250 95 | self.GIFWidget.setBackgroundColor(self.config['background_color']) 96 | self.GIFWidget.finish.connect(self.animateFinish) 97 | self.GIFWidget.moveDelta.connect(self.changeCenter) 98 | self.GIFWidget.setText(self.config['0']['words'], 'gift') 99 | w, h = self.GIFWidget.width(), self.GIFWidget.height() 100 | x, y = self.GIFWidget.pos().x(), self.GIFWidget.pos().y() 101 | self.GIFWidgetCenterPos = QPoint(x + w / 2, y + h / 2) 102 | 103 | self.setWindowTitle('DD答谢机 V1.0 (by 执明神君)') 104 | self.main_widget = QWidget() 105 | self.main_widget.setAcceptDrops(True) 106 | self.setCentralWidget(self.main_widget) 107 | layout = QGridLayout() 108 | self.main_widget.setLayout(layout) 109 | self.roomIDLabel = QLabel('B站直播房间') 110 | self.roomIDLabel.setAlignment(Qt.AlignCenter) 111 | layout.addWidget(self.roomIDLabel, 0, 0, 1, 1) 112 | self.biliLiveLabel = QLabel('https://live.bilibili.com/') 113 | layout.addWidget(self.biliLiveLabel, 0, 2, 1, 2) 114 | self.roomURLEdit = QLineEdit(self.config['room_url']) 115 | self.roomURLEdit.setValidator(QIntValidator(0, 2000000000)) 116 | self.roomURLEdit.textChanged.connect(self.setURL) 117 | layout.addWidget(self.roomURLEdit, 0, 4, 1, 3) 118 | 119 | self.presetCombobox = QComboBox() 120 | self.presetCombobox.addItems([' 礼物预设%d' % x for x in range(1, 10)] + [' 舰长上船']) 121 | self.presetCombobox.currentIndexChanged.connect(self.changePreset) 122 | layout.addWidget(self.presetCombobox, 1, 0, 1, 1) 123 | self.giftEdit = QLineEdit(self.gift) 124 | self.giftEdit.textChanged.connect(self.changeGift) 125 | layout.addWidget(self.giftEdit, 1, 2, 1, 5) 126 | 127 | self.defaultWordsButton = QPushButton('重置台词') 128 | self.defaultWordsButton.clicked.connect(self.setDefaultWords) 129 | layout.addWidget(self.defaultWordsButton, 2, 0, 1, 1) 130 | self.wordsEdit = QLineEdit(self.config['0']['words']) 131 | self.wordsEdit.textChanged.connect(self.setWords) 132 | layout.addWidget(self.wordsEdit, 2, 2, 1, 5) 133 | 134 | self.fontButton = QPushButton('字体设置') 135 | self.fontButton.clicked.connect(self.getFont) 136 | layout.addWidget(self.fontButton, 3, 0, 1, 1) 137 | self.fontLabel = QLabel() 138 | fontInfo = '%s %s %s ' % (self.config['0']['font_name'], self.config['0']['font_size'], self.config['0']['font_color']) 139 | self.font = QFont(self.config['0']['font_name'], self.config['0']['font_size']) 140 | if self.config['0']['font_bold']: 141 | self.font.setBold(self.config['0']['font_bold']) 142 | fontInfo += '加粗 ' 143 | if self.config['0']['font_italic']: 144 | self.font.setItalic(self.config['0']['font_italic']) 145 | fontInfo += '斜体' 146 | self.fontLabel.setText(fontInfo) 147 | self.fontLabel.setStyleSheet('color:' + self.color) 148 | self.GIFWidget.setFont(self.font) 149 | self.GIFWidget.setColor(self.color) 150 | layout.addWidget(self.fontLabel, 3, 2, 1, 5) 151 | 152 | self.outLineButton = QPushButton('描边颜色') 153 | self.outLineButton.clicked.connect(self.setOutLine) 154 | layout.addWidget(self.outLineButton, 4, 0, 1, 1) 155 | self.outLineLabel = QLabel(self.outColor) 156 | self.outLineLabel.setStyleSheet('color:' + self.outColor) 157 | layout.addWidget(self.outLineLabel, 4, 2, 1, 1) 158 | self.outLineSizeLabel = QLabel('粗细') 159 | self.outLineSizeLabel.setAlignment(Qt.AlignCenter) 160 | layout.addWidget(self.outLineSizeLabel, 4, 4, 1, 1) 161 | self.outLineSizeBar = Slider() 162 | self.outLineSizeBar.setMaximum(100) 163 | self.outLineSizeBar.setValue(self.outSize) 164 | self.outLineSizeBar.pointClicked.connect(self.sizeChange) 165 | layout.addWidget(self.outLineSizeBar, 4, 5, 1, 2) 166 | 167 | self.bgmButton = QPushButton('音效设置') 168 | self.bgmButton.clicked.connect(self.selectBGM) 169 | layout.addWidget(self.bgmButton, 5, 0, 1, 1) 170 | self.bgmEdit = QLabel(os.path.split(self.config['0']['bgm_path'])[1]) 171 | self.sound.setMedia(QUrl.fromLocalFile(self.config['0']['bgm_path'])) 172 | layout.addWidget(self.bgmEdit, 5, 2, 1, 2) 173 | self.volumeLabel = QLabel('音量') 174 | self.volumeLabel.setAlignment(Qt.AlignCenter) 175 | layout.addWidget(self.volumeLabel, 5, 4, 1, 1) 176 | self.volumeBar = Slider() 177 | self.volumeBar.setMaximum(100) 178 | self.volumeBar.setValue(self.volume) 179 | self.volumeBar.pointClicked.connect(self.changevolume) 180 | layout.addWidget(self.volumeBar, 5, 5, 1, 2) 181 | 182 | self.animeSecond = QLabel('持续时间') 183 | self.animeSecond.setAlignment(Qt.AlignCenter) 184 | layout.addWidget(self.animeSecond, 6, 0, 1, 1) 185 | self.animeSecondComboBox = QComboBox() 186 | self.animeSecondComboBox.addItems([str(i) + '秒' for i in range(1, 31)]) 187 | self.animeSecondComboBox.setCurrentIndex(self.second - 1) 188 | self.animeSecondComboBox.currentIndexChanged.connect(self.setSecond) 189 | layout.addWidget(self.animeSecondComboBox, 6, 2, 1, 2) 190 | 191 | self.scaledValue = QLabel('大小') 192 | self.scaledValue.setAlignment(Qt.AlignCenter) 193 | layout.addWidget(self.scaledValue, 6, 4, 1, 1) 194 | self.scaledBar = Slider() 195 | self.scaledBar.setValue(self.gifScale) 196 | self.scaledBar.pointClicked.connect(self.scaleChange) 197 | layout.addWidget(self.scaledBar, 6, 5, 1, 2) 198 | 199 | self.advancedButton = QPushButton('高级设置') 200 | self.advancedButton.clicked.connect(self.popOption) 201 | layout.addWidget(self.advancedButton, 7, 0, 1, 1) 202 | 203 | self.preview = previewLabel('点击或拖入要播放的答谢gif动图') 204 | self.preview.setFrameShape(QFrame.Box) 205 | self.preview.setStyleSheet('border:2px dotted #cfcfd0') 206 | self.preview.setMaximumSize(522, 400) 207 | self.preview.click.connect(self.click) 208 | if self.config['0']['gif_path']: 209 | self.gifSize = self.gifSizeList[0] 210 | self.movie = self.movieList[0] 211 | self.movie.setScaledSize(self.gifSize * self.gifScale / 50) 212 | self.preview.setMovie(self.movie) 213 | # self.GIFWidget.movie.setScaledSize(self.gifSize * self.gifScale / 50) 214 | self.movie.start() 215 | layout.addWidget(self.preview, 8, 0, 4, 7) 216 | 217 | self.testButton = QPushButton('预览效果') 218 | self.testButton.clicked.connect(self.testAnimate) 219 | self.testButton.setFixedSize(205, 65) 220 | layout.addWidget(self.testButton, 12, 0, 2, 3) 221 | 222 | self.startButton = QPushButton('开始捕获') 223 | self.startButton.clicked.connect(self.startMonitor) 224 | self.startButton.setFixedSize(205, 65) 225 | layout.addWidget(self.startButton, 12, 4, 2, 3) 226 | 227 | self.resizeTimer = QTimer() 228 | self.resizeTimer.setInterval(16) 229 | self.resizeTimer.timeout.connect(self.resizeGIFWidget) 230 | self.resizeTimer.start() 231 | 232 | self.widgetsList = [self.roomURLEdit, self.presetCombobox, self.giftEdit, self.defaultWordsButton, 233 | self.wordsEdit, self.fontButton, self.outLineButton, self.outLineSizeBar, 234 | self.bgmButton, self.volumeBar, self.animeSecondComboBox, self.scaledBar, 235 | self.advancedButton, self.preview, self.testButton, self.outLineSizeLabel, 236 | self.volumeLabel, self.scaledValue, self.animeSecond, self.bgmEdit, 237 | self.roomIDLabel, self.biliLiveLabel, self.fontLabel, self.outLineLabel] 238 | 239 | def resizeGIFWidget(self): 240 | self.GIFWidget.resize(300, 200) 241 | self.resize(540, 600) 242 | w, h = self.GIFWidget.width(), self.GIFWidget.height() 243 | x, y = self.GIFWidget.pos().x(), self.GIFWidget.pos().y() 244 | currentCenterPos = QPoint(x + w / 2, y + h / 2) 245 | self.GIFWidget.move(self.GIFWidget.pos() - currentCenterPos + self.GIFWidgetCenterPos) 246 | 247 | def changePreset(self, index): 248 | if index == 9: 249 | self.giftEdit.setEnabled(False) 250 | else: 251 | self.giftEdit.setEnabled(True) 252 | self.presetIndex = str(index) 253 | self.gift = self.config[self.presetIndex]['gift'] 254 | self.giftEdit.setText(self.gift) 255 | self.wordsEdit.setText(self.config[self.presetIndex]['words']) 256 | 257 | self.second = self.config[self.presetIndex]['second'] 258 | self.animeSecondComboBox.setCurrentIndex(self.second - 1) 259 | self.GIFWidget.setSecond(self.second) 260 | self.color = self.config[self.presetIndex]['font_color'] 261 | 262 | fontInfo = '%s %s %s ' % (self.config[self.presetIndex]['font_name'], 263 | self.config[self.presetIndex]['font_size'], 264 | self.config[self.presetIndex]['font_color']) 265 | self.font = QFont(self.config[self.presetIndex]['font_name'], 266 | self.config[self.presetIndex]['font_size']) 267 | if self.config[self.presetIndex]['font_bold']: 268 | self.font.setBold(self.config[self.presetIndex]['font_bold']) 269 | fontInfo += '加粗 ' 270 | if self.config[self.presetIndex]['font_italic']: 271 | self.font.setItalic(self.config[self.presetIndex]['font_italic']) 272 | fontInfo += '斜体' 273 | self.fontLabel.setText(fontInfo) 274 | self.fontLabel.setStyleSheet('color:' + self.color) 275 | self.GIFWidget.setFont(self.font) 276 | self.GIFWidget.showText.setBrush(QColor(self.color)) 277 | 278 | self.outColor = self.config[self.presetIndex]['out_color'] 279 | 280 | self.config[self.presetIndex]['out_color'] = self.outColor 281 | self.GIFWidget.showText.setPen(self.outColor) 282 | self.GIFWidget.showText.repaint() 283 | self.outLineLabel.setText(self.outColor) 284 | self.outLineLabel.setStyleSheet('color:' + self.outColor) 285 | 286 | self.outSize = self.config[self.presetIndex]['out_size'] 287 | 288 | self.outLineSizeBar.setValue(self.outSize) 289 | self.config[self.presetIndex]['out_size'] = self.outSize 290 | self.GIFWidget.showText.w = self.outSize / 250 291 | self.GIFWidget.showText.repaint() 292 | 293 | self.preview.clear() 294 | self.GIFWidget.showGIF.clear() 295 | self.preview.setText('点击或拖入要播放的答谢gif动图') 296 | if self.config[self.presetIndex]['gif_path']: 297 | self.movie = self.movieList[index] 298 | self.gifSize = self.gifSizeList[index] 299 | pos = self.config[self.presetIndex]['gif_scale'] 300 | self.scaledBar.setValue(pos) 301 | if self.gifSize: 302 | self.movie.setScaledSize(self.gifSize * pos / 50) 303 | self.preview.setMovie(self.movie) 304 | self.GIFWidget.showGIF.setMovie(self.movie) 305 | self.movie.start() 306 | 307 | self.bgmEdit.setText(os.path.split(self.config[self.presetIndex]['bgm_path'])[1]) 308 | self.sound.setMedia(QUrl.fromLocalFile(self.config[self.presetIndex]['bgm_path'])) 309 | 310 | self.volume = self.config[self.presetIndex]['volume'] 311 | self.volumeBar.setValue(self.volume) 312 | self.sound.setVolume(self.volume) 313 | 314 | def setURL(self, text): 315 | self.config['room_url'] = text 316 | 317 | def setWords(self, text): 318 | if self.presetIndex == '9': 319 | self.GIFWidget.setText(text, 'captain') 320 | else: 321 | self.GIFWidget.setText(text, 'gift') 322 | self.config[self.presetIndex]['words'] = text 323 | 324 | def setDefaultWords(self): 325 | if self.presetIndex == '9': 326 | text = '恭迎 {用户} 上舰~~~' 327 | self.GIFWidget.setText(text, 'captain') 328 | else: 329 | text = '感谢 {用户} 投喂的{数量}个{礼物}~' 330 | self.GIFWidget.setText(text, 'gift') 331 | self.wordsEdit.setText(text) 332 | self.config[self.presetIndex]['words'] = text 333 | 334 | def changeGift(self): 335 | self.config[self.presetIndex]['gift'] = self.giftEdit.text() 336 | 337 | def selectBGM(self): 338 | if not os.path.exists('bgm'): 339 | os.mkdir('bgm') 340 | filePath = QFileDialog.getOpenFileName(self, "请选择bgm文件", 'bgm', "*.mp3 *.wav")[0] 341 | if filePath: 342 | fileName = os.path.split(filePath)[1] 343 | if not os.path.exists(r'bgm/%s' % fileName): 344 | shutil.copy(filePath, 'bgm') 345 | self.bgmEdit.setText(os.path.split(filePath)[1]) 346 | self.sound.setMedia(QUrl.fromLocalFile(filePath)) 347 | self.config[self.presetIndex]['bgm_path'] = filePath 348 | 349 | def changevolume(self, p): 350 | pos = p.x() / self.volumeBar.width() * 100 351 | if pos > 100: 352 | pos = 100 353 | elif pos < 0: 354 | pos = 0 355 | self.volumeBar.setValue(pos) 356 | self.volume = pos 357 | self.sound.setVolume(pos) 358 | self.config[self.presetIndex]['volume'] = pos 359 | 360 | def selectBackgroundColor(self, color): 361 | self.config['background_color'] = color 362 | self.GIFWidget.setBackgroundColor(color) 363 | 364 | def setTop(self, topToken): 365 | self.config['top'] = topToken 366 | 367 | def setopacity(self, opacityToken): 368 | self.config['opacity'] = opacityToken 369 | 370 | def setSecond(self, index): 371 | self.second = index + 1 372 | self.GIFWidget.setSecond(self.second) 373 | self.config[self.presetIndex]['second'] = self.second 374 | 375 | def scaleChange(self, p): 376 | pos = p.x() / self.scaledBar.width() * 100 377 | if pos > 100: 378 | pos = 100 379 | elif pos < 1: 380 | pos = 1 381 | self.scaledBar.setValue(pos) 382 | self.config[self.presetIndex]['gif_scale'] = pos 383 | scale = pos / 50 384 | if self.gifSize: 385 | self.movie.setScaledSize(self.gifSize * scale) 386 | # self.GIFWidget.movie.setScaledSize(self.gifSize * scale) 387 | 388 | def sizeChange(self, p): 389 | self.outSize = p.x() / self.outLineSizeBar.width() * 100 390 | if self.outSize > 100: 391 | self.outSize = 100 392 | elif self.outSize < 1: 393 | self.outSize = 1 394 | self.outLineSizeBar.setValue(self.outSize) 395 | self.config[self.presetIndex]['out_size'] = self.outSize 396 | self.GIFWidget.showText.w = self.outSize / 400 397 | self.GIFWidget.showText.repaint() 398 | 399 | def click(self): 400 | filePath = QFileDialog.getOpenFileName(self, "请选择gif文件", 'gif', "*.gif;;所有文件 *.*")[0] 401 | if filePath: 402 | self.openFile(filePath) 403 | else: 404 | self.preview.clear() 405 | self.GIFWidget.showGIF.clear() 406 | self.preview.setText('点击或拖入要播放的答谢gif动图') 407 | self.config[self.presetIndex]['gif_path'] = '' 408 | 409 | def openFile(self, filePath): 410 | fileName = os.path.split(filePath)[1] 411 | if not os.path.exists('gif'): 412 | os.mkdir('gif') 413 | if not os.path.exists(r'gif/%s' % fileName): 414 | shutil.copy(filePath, 'gif') 415 | self.gifSize = QPixmap(filePath).size() 416 | index = self.presetCombobox.currentIndex() 417 | self.movieList[index] = QMovie(filePath) 418 | self.movie = self.movieList[index] 419 | self.preview.setMovie(self.movie) 420 | self.GIFWidget.showGIF.setMovie(self.movie) 421 | self.movie.start() 422 | # self.GIFWidget.setGIFPath(filePath) 423 | self.config[self.presetIndex]['gif_path'] = r'%s/gif/%s' % (os.getcwd(), fileName) 424 | self.scaledBar.setValue(50) 425 | 426 | def dragEnterEvent(self, QDragEnterEvent): 427 | QDragEnterEvent.accept() 428 | 429 | def dropEvent(self, QEvent): 430 | if QEvent.mimeData().hasUrls: 431 | self.openFile(QEvent.mimeData().urls()['0'].toLocalFile()) 432 | 433 | def closeEvent(self, QCloseEvent): 434 | self.GIFWidget.close() 435 | self.option.close() 436 | with codecs.open('utils/config.json', 'w', 'utf_8_sig') as config: 437 | config.write(json.dumps(self.config, ensure_ascii=False)) 438 | 439 | def getFont(self): 440 | status, font = QFontDialog.getFont(self.font) 441 | if status: 442 | font = QFontInfo(font) 443 | fontName = font.family() 444 | fontSize = font.pointSize() 445 | fontBold = font.bold() 446 | fontItalic = font.italic() 447 | self.font = QFont(fontName, fontSize) 448 | if fontBold: 449 | self.font.setBold(True) 450 | if fontItalic: 451 | self.font.setItalic(True) 452 | self.config[self.presetIndex]['font_name'] = fontName 453 | self.config[self.presetIndex]['font_size'] = fontSize 454 | self.config[self.presetIndex]['font_bold'] = fontBold 455 | self.config[self.presetIndex]['font_italic'] = fontItalic 456 | self.GIFWidget.setFont(self.font) 457 | fontInfo = '%s %s %s ' % (self.config[self.presetIndex]['font_name'], 458 | self.config[self.presetIndex]['font_size'], 459 | self.config[self.presetIndex]['font_color']) 460 | if fontBold: 461 | fontInfo += '加粗 ' 462 | if fontItalic: 463 | fontInfo += '斜体' 464 | self.fontLabel.setText(fontInfo) 465 | self.fontLabel.setStyleSheet('color:' + self.color) 466 | color = QColorDialog.getColor(self.color) 467 | if color.isValid(): 468 | self.color = color.name() 469 | self.config[self.presetIndex]['font_color'] = self.color 470 | self.GIFWidget.showText.setBrush(QColor(self.color)) 471 | self.GIFWidget.showText.repaint() 472 | 473 | def setOutLine(self): 474 | color = QColorDialog().getColor(self.outColor) 475 | if color.isValid(): 476 | self.outColor = color.name() 477 | self.config[self.presetIndex]['out_color'] = self.outColor 478 | self.GIFWidget.showText.setPen(self.outColor) 479 | self.GIFWidget.showText.repaint() 480 | self.outLineLabel.setText(self.outColor) 481 | self.outLineLabel.setStyleSheet('color:' + self.outColor) 482 | 483 | def setConfig(self, config, mode, index): 484 | self.GIFWidget.setText(config['words'], mode) 485 | 486 | # self.movie = self.movieList[index] 487 | # self.gifSize = self.gifSizeList[index] 488 | # if self.gifSize: 489 | # self.movie.setScaledSize(self.gifSize * config['gif_scale'] / 50) 490 | # self.GIFWidget.showGIF.setMovie(self.movie) 491 | # self.movie.start() 492 | 493 | self.GIFWidget.showGIF.clear() 494 | movie = self.movieList[index] 495 | gifSize = self.gifSizeList[index] 496 | if gifSize: 497 | movie.setScaledSize(gifSize * config['gif_scale'] / 50) 498 | self.GIFWidget.showGIF.setMovie(movie) 499 | # self.GIFWidget.movie = QMovie(config['gif_path']) 500 | # self.gifSize = QPixmap(config['gif_path']).size() 501 | # if self.gifSize: 502 | # self.GIFWidget.movie.setScaledSize(self.gifSize * config['gif_scale'] / 50) 503 | # self.GIFWidget.showGIF.setMovie(self.GIFWidget.movie) 504 | # self.GIFWidget.movie.start() 505 | self.GIFWidget.setSecond(config['second']) 506 | self.GIFWidget.showText.setBrush(QColor(config['font_color'])) 507 | self.GIFWidget.showText.setPen(config['out_color']) 508 | self.GIFWidget.showText.w = config['out_size'] / 400 509 | self.GIFWidget.showText.repaint() 510 | font = QFont(config['font_name'], config['font_size']) 511 | if config['font_bold']: 512 | font.setBold(True) 513 | if config['font_italic']: 514 | font.setItalic(True) 515 | self.GIFWidget.setFont(font) 516 | 517 | def testAnimate(self): 518 | if self.bgmEdit.text(): 519 | self.sound.stop() 520 | self.sound.play() 521 | self.GIFWidget.frame = self.second * 60 522 | self.GIFWidget.animationTimer.start() 523 | 524 | def playAnimate(self, giftInfo): 525 | uid, num, gift = giftInfo 526 | self.GIFWidget.ID = uid 527 | self.GIFWidget.number = str(num) 528 | self.GIFWidget.gift = gift 529 | self.GIFWidget.gifOpacity.setOpacity(0) 530 | self.GIFWidget.textOpacity.setOpacity(0) 531 | if gift == 'captain': 532 | self.setConfig(self.config['9'], 'captain', 9) 533 | self.GIFWidget.animationTimer.start() 534 | presetIndex = '9' 535 | else: 536 | for presetIndex in ['8', '7', '6', '5', '4', '3', '2', '1', '0']: 537 | if gift in self.config[presetIndex]['gift']: 538 | self.setConfig(self.config[presetIndex], 'gift', int(presetIndex)) 539 | self.GIFWidget.animationTimer.start() 540 | break 541 | bgm = self.config[presetIndex]['bgm_path'] 542 | if bgm: 543 | # if bgm != self.oldBGM: 544 | # self.oldBGM = bgm 545 | self.sound.setMedia(QUrl.fromLocalFile(bgm)) 546 | self.sound.setVolume(self.config[presetIndex]['volume']) 547 | self.sound.play() 548 | 549 | def popOption(self): 550 | self.option.hide() 551 | self.option.show() 552 | 553 | def animateFinish(self): 554 | self.sound.stop() 555 | 556 | def changeCenter(self, qpoint): 557 | self.GIFWidgetCenterPos += qpoint 558 | 559 | def startMonitor(self): 560 | if not self.executeToken: 561 | self.remoteThread = remoteThread(self.config['room_url']) 562 | self.remoteThread.giftInfo.connect(self.playAnimate) 563 | self.remoteThread.start() 564 | for widget in self.widgetsList: 565 | widget.setEnabled(False) 566 | self.executeToken = True 567 | self.GIFWidget.executeToken = True 568 | self.GIFWidget.gifOpacity.setOpacity(0) 569 | self.GIFWidget.textOpacity.setOpacity(0) 570 | self.startButton.setStyleSheet('background-color:#3daee9') 571 | self.startButton.setText('停止捕获') 572 | else: 573 | self.sound.stop() 574 | self.GIFWidget.animationTimer.stop() 575 | 576 | self.GIFWidget.showGIF.clear() 577 | self.GIFWidget.showGIF.setMovie(self.movie) 578 | 579 | self.remoteThread.terminate() 580 | self.remoteThread.quit() 581 | self.remoteThread.wait() 582 | for widget in self.widgetsList: 583 | widget.setEnabled(True) 584 | self.executeToken = False 585 | self.GIFWidget.executeToken = False 586 | self.GIFWidget.gifOpacity.setOpacity(1) 587 | self.GIFWidget.textOpacity.setOpacity(1) 588 | self.startButton.setStyleSheet('background-color:#31363b') 589 | self.startButton.setText('开始捕获') 590 | text = self.wordsEdit.text() 591 | if self.presetIndex == '9': 592 | self.GIFWidget.setText(text, 'captain', True) 593 | else: 594 | self.GIFWidget.setText(text, 'gift', True) 595 | 596 | 597 | if __name__ == '__main__': 598 | app = QApplication(sys.argv) 599 | qss = '' 600 | try: 601 | with open('utils/qdark.qss', 'r') as f: 602 | qss = f.read() 603 | except: 604 | print('警告!找不到QSS文件!') 605 | app.setStyleSheet(qss) 606 | app.setFont(QFont('微软雅黑', 9)) 607 | mainWindow = MainWindow() 608 | screen = app.primaryScreen().geometry() 609 | w, h = 540, 600 610 | mainWindow.resize(w, h) 611 | mainWindow.move((screen.width() - w) / 2 - 300, (screen.height() - h) / 2) 612 | mainWindow.show() 613 | mainWindow.GIFWidget.hide() 614 | mainWindow.GIFWidget.show() 615 | sys.exit(app.exec_()) 616 | -------------------------------------------------------------------------------- /GIFWidget.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import math 3 | from PySide2.QtWidgets import * 4 | from PySide2.QtGui import * 5 | from PySide2.QtCore import * 6 | 7 | 8 | class OutlinedLabel(QLabel): 9 | def __init__(self, text='', fontColor='#FFFFFF', outColor='#FFFFFF'): 10 | super().__init__() 11 | self.setText(text) 12 | self.w = 1 / 10 13 | self.mode = True 14 | self.setBrush(QColor(fontColor)) 15 | self.setPen(outColor) 16 | 17 | def scaledOutlineMode(self): 18 | return self.mode 19 | 20 | def setScaledOutlineMode(self, state): 21 | self.mode = state 22 | 23 | def outlineThickness(self): 24 | return self.w * self.font().pointSize() if self.mode else self.w 25 | 26 | def setOutlineThickness(self, value): 27 | self.w = value 28 | 29 | def setBrush(self, brush): 30 | if not isinstance(brush, QBrush): 31 | brush = QBrush(brush) 32 | self.brush = brush 33 | 34 | def setPen(self, pen): 35 | if not isinstance(pen, QPen): 36 | pen = QPen(pen) 37 | pen.setJoinStyle(Qt.RoundJoin) 38 | self.pen = pen 39 | 40 | def sizeHint(self): 41 | w = math.ceil(self.outlineThickness() * 2) 42 | return super().sizeHint() + QSize(w, w) 43 | 44 | def minimumSizeHint(self): 45 | w = math.ceil(self.outlineThickness() * 2) 46 | return super().minimumSizeHint() + QSize(w, w) 47 | 48 | def paintEvent(self, event): 49 | w = self.outlineThickness() 50 | rect = self.rect() 51 | metrics = QFontMetrics(self.font()) 52 | tr = metrics.boundingRect(self.text()).adjusted(0, 0, w, w) 53 | if self.indent() == -1: 54 | if self.frameWidth(): 55 | indent = (metrics.boundingRect('x').width() + w * 2) / 2 56 | else: 57 | indent = w 58 | else: 59 | indent = self.indent() 60 | 61 | if self.alignment() & Qt.AlignLeft: 62 | x = rect.left() + indent - min(metrics.leftBearing(self.text()['0']), 0) 63 | elif self.alignment() & Qt.AlignRight: 64 | x = rect.x() + rect.width() - indent - tr.width() 65 | else: 66 | x = (rect.width() - tr.width()) / 2 67 | 68 | if self.alignment() & Qt.AlignTop: 69 | y = rect.top() + indent + metrics.ascent() 70 | elif self.alignment() & Qt.AlignBottom: 71 | y = rect.y() + rect.height() - indent - metrics.descent() 72 | else: 73 | y = (rect.height() + metrics.ascent() - metrics.descent()) / 2 74 | 75 | path = QPainterPath() 76 | path.addText(x, y, self.font(), self.text()) 77 | qp = QPainter(self) 78 | qp.setRenderHint(QPainter.Antialiasing) 79 | 80 | self.pen.setWidthF(w * 2) 81 | qp.strokePath(path, self.pen) 82 | if 1 < self.brush.style() < 15: 83 | qp.fillPath(path, self.palette().window()) 84 | qp.fillPath(path, self.brush) 85 | 86 | 87 | class GIFWidget(QWidget): 88 | finish = Signal() 89 | moveDelta = Signal(QPoint) 90 | 91 | def __init__(self, qmovie, opacity=False, top=True, frame=180, 92 | fontColor='#000000', outColor='#FFFFFF', parent=None): 93 | super().__init__(parent) 94 | self.mousePressToken = False 95 | self.executeToken = False 96 | self.ID = 'DD' 97 | self.number = '100' 98 | self.gift = '小心心' 99 | self.setWindowTitle('答谢特效') 100 | if opacity: 101 | self.setAttribute(Qt.WA_TranslucentBackground) 102 | self.setWindowFlag(Qt.FramelessWindowHint) 103 | if top: 104 | self.setWindowFlag(Qt.WindowStaysOnTopHint) 105 | self.show() 106 | self.layout = QGridLayout() 107 | self.setLayout(self.layout) 108 | 109 | self.text = ' 感谢 DD 投喂的100个小心心 ' 110 | self.showText = OutlinedLabel(self.text, fontColor, outColor) 111 | self.showText.setAlignment(Qt.AlignCenter) 112 | self.showText.setAttribute(Qt.WA_TranslucentBackground) 113 | self.layout.addWidget(self.showText, 1, 0, 1, 1) 114 | self.textOpacity = QGraphicsOpacityEffect() 115 | self.showText.setGraphicsEffect(self.textOpacity) 116 | self.textOpacity.setOpacity(1) 117 | 118 | self.showGIF = QLabel() 119 | self.showGIF.setAttribute(Qt.WA_TranslucentBackground) 120 | self.showGIF.setAlignment(Qt.AlignCenter) 121 | self.layout.addWidget(self.showGIF, 0, 0, 1, 1) 122 | # if gifPath: 123 | # self.movie = QMovie(gifPath) 124 | # self.showGIF.setMovie(self.movie) 125 | # self.movie.start() 126 | self.showGIF.setMovie(qmovie) 127 | self.gifOpacity = QGraphicsOpacityEffect() 128 | self.showGIF.setGraphicsEffect(self.gifOpacity) 129 | self.gifOpacity.setOpacity(1) 130 | 131 | self.totalFrame = frame 132 | self.frame = self.totalFrame 133 | self.animationTimer = QTimer() 134 | self.animationTimer.setInterval(16) 135 | self.animationTimer.timeout.connect(self.playAnimate) 136 | 137 | def setGIFPath(self, gifPath): 138 | self.movie = QMovie(gifPath) 139 | self.showGIF.setMovie(self.movie) 140 | self.movie.start() 141 | 142 | def setText(self, text, mode, default=False): 143 | if default: 144 | self.ID = 'DD' 145 | self.number = '100' 146 | self.gift = '小心心' 147 | if mode == 'gift': 148 | text = ' %s ' % text.replace('{用户}', self.ID).replace('{数量}', self.number).replace('{礼物}', self.gift) 149 | self.showText.setText(text) 150 | elif mode == 'captain': 151 | text = ' %s ' % text.replace('{用户}', self.ID) 152 | self.showText.setText(text) 153 | 154 | def setFont(self, font): 155 | metrics = QFontMetrics(font) 156 | path = QPainterPath() 157 | pen = QPen(Qt.red) 158 | penwidth = 2 159 | pen.setWidth(penwidth) 160 | l = metrics.width(self.text) 161 | w = self.showText.width() 162 | px = (l - w) / 2 163 | if px < 0: 164 | px = -px 165 | py = (self.showText.height() - metrics.height()) / 2 + metrics.ascent() 166 | if py < 0: 167 | py = -py 168 | path.addText(px, py, font, self.text) 169 | painter = QPainter() 170 | painter.strokePath(path, pen) 171 | painter.drawPath(path) 172 | painter.fillPath(path, QBrush(Qt.red)) 173 | self.showText.setFont(font) 174 | 175 | def setColor(self, color): 176 | self.showText.setStyleSheet('color:' + color) 177 | 178 | def setBackgroundColor(self, color): 179 | self.setStyleSheet('background-color:%s' % color) 180 | 181 | def setGiftInfo(self, giftInfo): 182 | self.text = ' 感谢 %s 投喂的%s个%s ' % tuple(giftInfo) 183 | self.showText.setText(self.text) 184 | 185 | def setGuard(self, guardInfo): 186 | self.text = '%s %s %s' % tuple(guardInfo) 187 | self.showText.setText(self.text) 188 | 189 | def setSecond(self, seconds): 190 | self.frame = seconds * 60 191 | self.totalFrame = seconds * 60 192 | 193 | def mousePressEvent(self, QEvent): 194 | self.mousePressToken = True 195 | self.startPos = QEvent.pos() 196 | 197 | def mouseReleaseEvent(self, QEvent): 198 | self.mousePressToken = False 199 | 200 | def mouseMoveEvent(self, QEvent): 201 | if self.mousePressToken: 202 | self.move(self.pos() + (QEvent.pos() - self.startPos)) 203 | self.moveDelta.emit(QEvent.pos() - self.startPos) 204 | 205 | def playAnimate(self): 206 | if self.frame >= self.totalFrame - 5: 207 | self.gifOpacity.setOpacity(0) 208 | self.textOpacity.setOpacity(0) 209 | self.frame -= 1 210 | elif self.frame >= self.totalFrame - 15: 211 | self.gifOpacity.setOpacity((self.totalFrame - self.frame - 5) / 10) 212 | self.textOpacity.setOpacity((self.totalFrame - self.frame - 5) / 10) 213 | self.frame -= 1 214 | elif self.frame > 40: 215 | self.frame -= 1 216 | elif self.frame > 0: 217 | self.gifOpacity.setOpacity(self.frame / 40) 218 | self.textOpacity.setOpacity(self.frame / 40) 219 | self.frame -= 1 220 | else: 221 | self.frame = self.totalFrame 222 | self.animationTimer.stop() 223 | if not self.executeToken: 224 | self.gifOpacity.setOpacity(1) 225 | self.textOpacity.setOpacity(1) 226 | else: 227 | self.gifOpacity.setOpacity(0) 228 | self.textOpacity.setOpacity(0) 229 | self.finish.emit() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bilibili_gifts_thanks 2 | 仿照老鼠台礼物答谢特效而制作的B站直播礼物答谢机 带GUI界面 操作简单 可以自定义直播间 特效动图 答谢音效 字体及颜色 以及过滤银币礼物的功能 3 | 4 | B站使用教程视频 5 | https://www.bilibili.com/video/BV1Di4y1L7T2/ 6 | -------------------------------------------------------------------------------- /bgm/撒钱.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhimingshenjun/bilibili_gifts_thanks/363fcade943483ca4af192c4eaa86f974afcc8b1/bgm/撒钱.mp3 -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | {"room_url": "https://live.bilibili.com/21919321?visit_id=75sr2kbj4vg0", "bgm_path": "C:/workspace/Python教程系列/B站直播礼物答谢效果/bgm/撒钱.mp3", "gif_path": "C:\\workspace\\Python教程系列\\B站直播礼物答谢效果/gif/151b09b4713cb579dc5e41257efdad2afb968331.gif", "font_color": "#ca5c66", "font_name": "微软雅黑", "font_size": 24, "font_bold": "1", "font_italic": "0", "guard_text_before": "恭迎", "guard_text_after": "舰长登船~~", "filter": "0", "background_color": "#00d600", "opacity_color": "0", "top": "0"} -------------------------------------------------------------------------------- /gif/151b09b4713cb579dc5e41257efdad2afb968331.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhimingshenjun/bilibili_gifts_thanks/363fcade943483ca4af192c4eaa86f974afcc8b1/gif/151b09b4713cb579dc5e41257efdad2afb968331.gif -------------------------------------------------------------------------------- /gif/清良.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhimingshenjun/bilibili_gifts_thanks/363fcade943483ca4af192c4eaa86f974afcc8b1/gif/清良.gif -------------------------------------------------------------------------------- /option.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PySide2.QtWidgets import * 3 | from PySide2.QtCore import * 4 | 5 | 6 | def _translate(context, text, disambig): 7 | return QApplication.translate(context, text, disambig) 8 | 9 | 10 | class OptionWidget(QWidget): 11 | color = Signal(str) 12 | opacity = Signal(bool) 13 | top = Signal(bool) 14 | 15 | def __init__(self, color, opacityToken, topToken): 16 | super().__init__() 17 | self.resize(540, 130) 18 | self.setWindowTitle('高级设置') 19 | layout = QGridLayout() 20 | self.setLayout(layout) 21 | backgroundColorButton = QPushButton('背景颜色') 22 | backgroundColorButton.clicked.connect(self.selectBackgroundColor) 23 | layout.addWidget(backgroundColorButton, 0, 0, 1, 1) 24 | self.backgroundColorLabel = QLabel(color) 25 | self.backgroundColorLabel.setStyleSheet('color:%s' % color) 26 | layout.addWidget(self.backgroundColorLabel, 0, 1, 1, 1) 27 | 28 | self.opacityButton = QPushButton('透明背景 (需重启)') 29 | self.opacityButton.clicked.connect(self.setOpacity) 30 | self.opacityToken = opacityToken 31 | if self.opacityToken: 32 | self.opacityButton.setStyleSheet('background-color:#3daee9') 33 | else: 34 | self.opacityButton.setStyleSheet('background-color:#31363b') 35 | layout.addWidget(self.opacityButton, 0, 2, 1, 2) 36 | 37 | self.stayTopButton = QPushButton('特效置顶 (需重启)') 38 | self.stayTopButton.clicked.connect(self.setTop) 39 | self.topToken = topToken 40 | if self.topToken: 41 | self.stayTopButton.setStyleSheet('background-color:#3daee9') 42 | else: 43 | self.stayTopButton.setStyleSheet('background-color:#31363b') 44 | layout.addWidget(self.stayTopButton, 0, 4, 1, 2) 45 | 46 | layout.addWidget(QLabel('更多使用教程 请访问'), 1, 0, 1, 2) 47 | 48 | bilibili_url = QLabel() 49 | bilibili_url.setOpenExternalLinks(True) 50 | bilibili_url.setText(_translate("MainWindow", "
", 52 | None)) 53 | layout.addWidget(bilibili_url, 1, 2, 1, 4) 54 | 55 | def selectBackgroundColor(self): 56 | color = QColorDialog.getColor(self.backgroundColorLabel.text()) 57 | if color.isValid(): 58 | color = color.name() 59 | self.color.emit(color) 60 | self.backgroundColorLabel.setText(color) 61 | self.backgroundColorLabel.setStyleSheet('color:%s' % color) 62 | 63 | def setOpacity(self): 64 | self.opacityToken = not self.opacityToken 65 | if self.opacityToken: 66 | self.opacityButton.setStyleSheet('background-color:#3daee9') 67 | else: 68 | self.opacityButton.setStyleSheet('background-color:#31363b') 69 | self.opacity.emit(self.opacityToken) 70 | 71 | def setTop(self): 72 | self.topToken = not self.topToken 73 | if self.topToken: 74 | self.stayTopButton.setStyleSheet('background-color:#3daee9') 75 | else: 76 | self.stayTopButton.setStyleSheet('background-color:#31363b') 77 | self.top.emit(self.topToken) 78 | -------------------------------------------------------------------------------- /remote.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import asyncio 3 | import zlib 4 | import json 5 | import requests 6 | from aiowebsocket.converses import AioWebSocket 7 | from PySide2.QtCore import * 8 | 9 | 10 | class remoteThread(QThread): 11 | giftInfo = Signal(list) 12 | 13 | def __init__(self, roomID): 14 | super(remoteThread, self).__init__() 15 | self.roomID = roomID 16 | if len(self.roomID) <= 3: 17 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 \ 18 | (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE'} 19 | html = requests.get('https://live.bilibili.com/' + self.roomID, headers=headers).text 20 | for line in html.split('\n'): 21 | if '"roomid":' in line: 22 | self.roomID = line.split('"roomid":')[1].split(',')[0] 23 | 24 | async def startup(self, url): 25 | data_raw = '000000{headerLen}0010000100000007000000017b22726f6f6d6964223a{roomid}7d' 26 | data_raw = data_raw.format(headerLen=hex(27 + len(self.roomID))[2:], 27 | roomid=''.join(map(lambda x: hex(ord(x))[2:], list(self.roomID)))) 28 | async with AioWebSocket(url) as aws: 29 | converse = aws.manipulator 30 | await converse.send(bytes.fromhex(data_raw)) 31 | tasks = [self.receDM(converse), self.sendHeartBeat(converse)] 32 | await asyncio.wait(tasks) 33 | 34 | async def sendHeartBeat(self, websocket): 35 | hb = '00000010001000010000000200000001' 36 | while True: 37 | await asyncio.sleep(30) 38 | await websocket.send(bytes.fromhex(hb)) 39 | 40 | async def receDM(self, websocket): 41 | while True: 42 | recv_text = await websocket.receive() 43 | self.printDM(recv_text) 44 | 45 | def printDM(self, data): 46 | packetLen = int(data[:4].hex(), 16) 47 | ver = int(data[6:8].hex(), 16) 48 | op = int(data[8:12].hex(), 16) 49 | 50 | if len(data) > packetLen: 51 | self.printDM(data[packetLen:]) 52 | data = data[:packetLen] 53 | 54 | if ver == 2: 55 | data = zlib.decompress(data[16:]) 56 | self.printDM(data) 57 | return 58 | 59 | if ver == 1: 60 | if op == 3: 61 | # print('[RENQI] {}'.format(int(data[16:].hex(),16))) 62 | pass 63 | return 64 | 65 | if op == 5: 66 | try: 67 | jd = json.loads(data[16:].decode('utf-8', errors='ignore')) 68 | if jd['cmd'] == 'SEND_GIFT': 69 | d = jd['data'] 70 | self.giftInfo.emit([d['uname'], d['num'], d['giftName']]) 71 | elif jd['cmd'] == 'COMBO_SEND': 72 | d = jd['data'] 73 | self.giftInfo.emit([d['uname'], d['batch_combo_num'], d['gift_name']]) 74 | elif jd['cmd'] == 'GUARD_BUY': 75 | self.giftInfo.emit([jd['data']['username'], '1', 'captain']) 76 | except Exception as e: 77 | print(e) 78 | 79 | def run(self): 80 | remote = r'wss://broadcastlv.chat.bilibili.com:2245/sub' 81 | try: 82 | asyncio.set_event_loop(asyncio.new_event_loop()) 83 | asyncio.get_event_loop().run_until_complete(self.startup(remote)) 84 | except KeyboardInterrupt as e: 85 | print('exit') -------------------------------------------------------------------------------- /utils/qdark.qss: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License 3 | 4 | Copyright (c) 2013-2018 Colin Duquesnoy https://github.com/ColinDuquesnoy/QDarkStyleSheet/blob/master/LICENSE.md 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to 9 | whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 12 | Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 15 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 18 | OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | 22 | QToolTip { 23 | border: 1px solid #76797C; 24 | background-color: #5A7566; 25 | color: white; 26 | padding: 0px; /*remove padding, for fix combobox tooltip.*/ 27 | opacity: 200; 28 | } 29 | 30 | QWidget { 31 | color: #eff0f1; 32 | background-color: #31363b; 33 | selection-background-color: #3daee9; 34 | selection-color: #eff0f1; 35 | background-clip: border; 36 | border-image: none; 37 | border: 0px transparent black; 38 | outline: 0; 39 | } 40 | 41 | QWidget:item:hover { 42 | background-color: #18465d; 43 | color: #eff0f1; 44 | } 45 | 46 | QWidget:item:selected { 47 | background-color: #18465d; 48 | } 49 | 50 | QGroupBox::indicator { 51 | width: 18px; 52 | height: 18px; 53 | } 54 | 55 | QGroupBox::indicator { 56 | margin-left: 2px; 57 | } 58 | 59 | QGroupBox::indicator:unchecked { 60 | image: url(:/qss_icons/rc/checkbox_unchecked.png); 61 | } 62 | 63 | QGroupBox::indicator:unchecked:hover, 64 | QGroupBox::indicator:unchecked:focus, 65 | QGroupBox::indicator:unchecked:pressed { 66 | border: none; 67 | image: url(:/qss_icons/rc/checkbox_unchecked_focus.png); 68 | } 69 | 70 | QGroupBox::indicator:checked { 71 | image: url(:/qss_icons/rc/checkbox_checked.png); 72 | } 73 | 74 | QGroupBox::indicator:checked:hover, 75 | QGroupBox::indicator:checked:focus, 76 | QGroupBox::indicator:checked:pressed { 77 | border: none; 78 | image: url(:/qss_icons/rc/checkbox_checked_focus.png); 79 | } 80 | 81 | QCheckBox::indicator:indeterminate { 82 | image: url(:/qss_icons/rc/checkbox_indeterminate.png); 83 | } 84 | 85 | QCheckBox::indicator:indeterminate:focus, 86 | QCheckBox::indicator:indeterminate:hover, 87 | QCheckBox::indicator:indeterminate:pressed { 88 | image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png); 89 | } 90 | 91 | QCheckBox::indicator:checked:disabled, 92 | QGroupBox::indicator:checked:disabled { 93 | image: url(:/qss_icons/rc/checkbox_checked_disabled.png); 94 | } 95 | 96 | QCheckBox::indicator:unchecked:disabled, 97 | QGroupBox::indicator:unchecked:disabled { 98 | image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); 99 | } 100 | 101 | QRadioButton { 102 | spacing: 5px; 103 | outline: none; 104 | color: #eff0f1; 105 | margin-bottom: 2px; 106 | } 107 | 108 | QRadioButton:disabled { 109 | color: #76797C; 110 | } 111 | 112 | QRadioButton::indicator { 113 | width: 21px; 114 | height: 21px; 115 | } 116 | 117 | QRadioButton::indicator:unchecked { 118 | image: url(:/qss_icons/rc/radio_unchecked.png); 119 | } 120 | 121 | QRadioButton::indicator:unchecked:hover, 122 | QRadioButton::indicator:unchecked:focus, 123 | QRadioButton::indicator:unchecked:pressed { 124 | border: none; 125 | outline: none; 126 | image: url(:/qss_icons/rc/radio_unchecked_focus.png); 127 | } 128 | 129 | QRadioButton::indicator:checked { 130 | border: none; 131 | outline: none; 132 | image: url(:/qss_icons/rc/radio_checked.png); 133 | } 134 | 135 | QRadioButton::indicator:checked:hover, 136 | QRadioButton::indicator:checked:focus, 137 | QRadioButton::indicator:checked:pressed { 138 | border: none; 139 | outline: none; 140 | image: url(:/qss_icons/rc/radio_checked_focus.png); 141 | } 142 | 143 | QRadioButton::indicator:checked:disabled { 144 | outline: none; 145 | image: url(:/qss_icons/rc/radio_checked_disabled.png); 146 | } 147 | 148 | QRadioButton::indicator:unchecked:disabled { 149 | image: url(:/qss_icons/rc/radio_unchecked_disabled.png); 150 | } 151 | 152 | QMenuBar { 153 | background-color: #31363b; 154 | color: #eff0f1; 155 | } 156 | 157 | QMenuBar::item { 158 | background: transparent; 159 | } 160 | 161 | QMenuBar::item:selected { 162 | background: transparent; 163 | border: 1px solid #76797C; 164 | } 165 | 166 | QMenuBar::item:pressed { 167 | border: 1px solid #76797C; 168 | background-color: #3daee9; 169 | color: #eff0f1; 170 | margin-bottom: -1px; 171 | padding-bottom: 1px; 172 | } 173 | 174 | QMenu { 175 | border: 1px solid #76797C; 176 | color: #eff0f1; 177 | margin: 2px; 178 | } 179 | 180 | QMenu::icon { 181 | margin: 5px; 182 | } 183 | 184 | QMenu::item { 185 | padding: 5px 30px 5px 30px; 186 | border: 1px solid transparent; 187 | /* reserve space for selection border */ 188 | } 189 | 190 | QMenu::item:selected { 191 | color: #eff0f1; 192 | } 193 | 194 | QMenu::separator { 195 | height: 2px; 196 | background: lightblue; 197 | margin-left: 10px; 198 | margin-right: 5px; 199 | } 200 | 201 | QMenu::indicator { 202 | width: 18px; 203 | height: 18px; 204 | } 205 | 206 | 207 | /* non-exclusive indicator = check box style indicator 208 | (see QActionGroup::setExclusive) */ 209 | 210 | QMenu::indicator:non-exclusive:unchecked { 211 | image: url(:/qss_icons/rc/checkbox_unchecked.png); 212 | } 213 | 214 | QMenu::indicator:non-exclusive:unchecked:selected { 215 | image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); 216 | } 217 | 218 | QMenu::indicator:non-exclusive:checked { 219 | image: url(:/qss_icons/rc/checkbox_checked.png); 220 | } 221 | 222 | QMenu::indicator:non-exclusive:checked:selected { 223 | image: url(:/qss_icons/rc/checkbox_checked_disabled.png); 224 | } 225 | 226 | 227 | /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ 228 | 229 | QMenu::indicator:exclusive:unchecked { 230 | image: url(:/qss_icons/rc/radio_unchecked.png); 231 | } 232 | 233 | QMenu::indicator:exclusive:unchecked:selected { 234 | image: url(:/qss_icons/rc/radio_unchecked_disabled.png); 235 | } 236 | 237 | QMenu::indicator:exclusive:checked { 238 | image: url(:/qss_icons/rc/radio_checked.png); 239 | } 240 | 241 | QMenu::indicator:exclusive:checked:selected { 242 | image: url(:/qss_icons/rc/radio_checked_disabled.png); 243 | } 244 | 245 | QMenu::right-arrow { 246 | margin: 5px; 247 | image: url(:/qss_icons/rc/right_arrow.png) 248 | } 249 | 250 | QWidget:disabled { 251 | color: #454545; 252 | background-color: #31363b; 253 | } 254 | 255 | QAbstractItemView { 256 | alternate-background-color: #31363b; 257 | color: #eff0f1; 258 | border: 1px solid #3A3939; 259 | border-radius: 2px; 260 | } 261 | 262 | QWidget:focus, 263 | QMenuBar:focus { 264 | border: 1px solid #3daee9; 265 | } 266 | 267 | QTabWidget:focus, 268 | QCheckBox:focus, 269 | QRadioButton:focus, 270 | QSlider:focus { 271 | border: none; 272 | } 273 | 274 | QLineEdit { 275 | background-color: #232629; 276 | padding: 5px; 277 | border-style: solid; 278 | border: 1px solid #76797C; 279 | border-radius: 2px; 280 | color: #eff0f1; 281 | } 282 | 283 | QAbstractItemView QLineEdit { 284 | padding: 0; 285 | } 286 | 287 | QGroupBox { 288 | border: 1px solid #76797C; 289 | border-radius: 2px; 290 | margin-top: 20px; 291 | } 292 | 293 | QGroupBox::title { 294 | subcontrol-origin: margin; 295 | subcontrol-position: top center; 296 | padding-left: 10px; 297 | padding-right: 10px; 298 | padding-top: 10px; 299 | } 300 | 301 | QAbstractScrollArea { 302 | border-radius: 2px; 303 | border: 1px solid #76797C; 304 | background-color: transparent; 305 | } 306 | 307 | QScrollBar:horizontal { 308 | height: 15px; 309 | margin: 3px 15px 3px 15px; 310 | border: 1px transparent #2A2929; 311 | border-radius: 4px; 312 | background-color: #2A2929; 313 | } 314 | 315 | QScrollBar::handle:horizontal { 316 | background-color: #605F5F; 317 | min-width: 5px; 318 | border-radius: 4px; 319 | } 320 | 321 | QScrollBar::add-line:horizontal { 322 | margin: 0px 3px 0px 3px; 323 | border-image: url(:/qss_icons/rc/right_arrow_disabled.png); 324 | width: 10px; 325 | height: 10px; 326 | subcontrol-position: right; 327 | subcontrol-origin: margin; 328 | } 329 | 330 | QScrollBar::sub-line:horizontal { 331 | margin: 0px 3px 0px 3px; 332 | border-image: url(:/qss_icons/rc/left_arrow_disabled.png); 333 | height: 10px; 334 | width: 10px; 335 | subcontrol-position: left; 336 | subcontrol-origin: margin; 337 | } 338 | 339 | QScrollBar::add-line:horizontal:hover, 340 | QScrollBar::add-line:horizontal:on { 341 | border-image: url(:/qss_icons/rc/right_arrow.png); 342 | height: 10px; 343 | width: 10px; 344 | subcontrol-position: right; 345 | subcontrol-origin: margin; 346 | } 347 | 348 | QScrollBar::sub-line:horizontal:hover, 349 | QScrollBar::sub-line:horizontal:on { 350 | border-image: url(:/qss_icons/rc/left_arrow.png); 351 | height: 10px; 352 | width: 10px; 353 | subcontrol-position: left; 354 | subcontrol-origin: margin; 355 | } 356 | 357 | QScrollBar::up-arrow:horizontal, 358 | QScrollBar::down-arrow:horizontal { 359 | background: none; 360 | } 361 | 362 | QScrollBar::add-page:horizontal, 363 | QScrollBar::sub-page:horizontal { 364 | background: none; 365 | } 366 | 367 | QScrollBar:vertical { 368 | background-color: #2A2929; 369 | width: 15px; 370 | margin: 15px 3px 15px 3px; 371 | border: 1px transparent #2A2929; 372 | border-radius: 4px; 373 | } 374 | 375 | QScrollBar::handle:vertical { 376 | background-color: #605F5F; 377 | min-height: 40px; 378 | border-radius: 4px; 379 | } 380 | 381 | QScrollBar::sub-line:vertical { 382 | margin: 3px 0px 3px 0px; 383 | border-image: url(:/qss_icons/rc/up_arrow_disabled.png); 384 | height: 10px; 385 | width: 10px; 386 | subcontrol-position: top; 387 | subcontrol-origin: margin; 388 | } 389 | 390 | QScrollBar::add-line:vertical { 391 | margin: 3px 0px 3px 0px; 392 | border-image: url(:/qss_icons/rc/down_arrow_disabled.png); 393 | height: 10px; 394 | width: 10px; 395 | subcontrol-position: bottom; 396 | subcontrol-origin: margin; 397 | } 398 | 399 | QScrollBar::sub-line:vertical:hover, 400 | QScrollBar::sub-line:vertical:on { 401 | border-image: url(:/qss_icons/rc/up_arrow.png); 402 | height: 10px; 403 | width: 10px; 404 | subcontrol-position: top; 405 | subcontrol-origin: margin; 406 | } 407 | 408 | QScrollBar::add-line:vertical:hover, 409 | QScrollBar::add-line:vertical:on { 410 | border-image: url(:/qss_icons/rc/down_arrow.png); 411 | height: 10px; 412 | width: 10px; 413 | subcontrol-position: bottom; 414 | subcontrol-origin: margin; 415 | } 416 | 417 | QScrollBar::up-arrow:vertical, 418 | QScrollBar::down-arrow:vertical { 419 | background: none; 420 | } 421 | 422 | QScrollBar::add-page:vertical, 423 | QScrollBar::sub-page:vertical { 424 | background: none; 425 | } 426 | 427 | QTextEdit { 428 | background-color: #232629; 429 | color: #eff0f1; 430 | border: 1px solid #76797C; 431 | } 432 | 433 | QPlainTextEdit { 434 | background-color: #232629; 435 | ; 436 | color: #eff0f1; 437 | border-radius: 2px; 438 | border: 1px solid #76797C; 439 | } 440 | 441 | QHeaderView::section { 442 | background-color: #76797C; 443 | color: #eff0f1; 444 | padding: 5px; 445 | border: 1px solid #76797C; 446 | } 447 | 448 | QSizeGrip { 449 | image: url(:/qss_icons/rc/sizegrip.png); 450 | width: 12px; 451 | height: 12px; 452 | } 453 | 454 | QMainWindow::separator { 455 | background-color: #31363b; 456 | color: white; 457 | padding-left: 4px; 458 | spacing: 2px; 459 | border: 1px dashed #76797C; 460 | } 461 | 462 | QMainWindow::separator:hover { 463 | background-color: #787876; 464 | color: white; 465 | padding-left: 4px; 466 | border: 1px solid #76797C; 467 | spacing: 2px; 468 | } 469 | 470 | QMenu::separator { 471 | height: 1px; 472 | background-color: #76797C; 473 | color: white; 474 | padding-left: 4px; 475 | margin-left: 10px; 476 | margin-right: 5px; 477 | } 478 | 479 | QFrame { 480 | border-radius: 2px; 481 | border: 1px solid #76797C; 482 | } 483 | 484 | QFrame[frameShape="0"] { 485 | border-radius: 2px; 486 | border: 1px transparent #76797C; 487 | } 488 | 489 | QStackedWidget { 490 | border: 1px transparent black; 491 | } 492 | 493 | QToolBar { 494 | border: 1px transparent #393838; 495 | background: 1px solid #31363b; 496 | font-weight: bold; 497 | } 498 | 499 | QToolBar::handle:horizontal { 500 | image: url(:/qss_icons/rc/Hmovetoolbar.png); 501 | } 502 | 503 | QToolBar::handle:vertical { 504 | image: url(:/qss_icons/rc/Vmovetoolbar.png); 505 | } 506 | 507 | QToolBar::separator:horizontal { 508 | image: url(:/qss_icons/rc/Hsepartoolbar.png); 509 | } 510 | 511 | QToolBar::separator:vertical { 512 | image: url(:/qss_icons/rc/Vsepartoolbar.png); 513 | } 514 | 515 | QToolButton#qt_toolbar_ext_button { 516 | background: #58595a 517 | } 518 | 519 | QPushButton { 520 | color: #eff0f1; 521 | background-color: #31363b; 522 | border-width: 1px; 523 | border-color: #76797C; 524 | border-style: solid; 525 | padding: 5px; 526 | border-radius: 2px; 527 | outline: none; 528 | } 529 | 530 | QPushButton:disabled { 531 | background-color: #31363b; 532 | border-width: 1px; 533 | border-color: #454545; 534 | border-style: solid; 535 | padding-top: 5px; 536 | padding-bottom: 5px; 537 | padding-left: 10px; 538 | padding-right: 10px; 539 | border-radius: 2px; 540 | color: #454545; 541 | } 542 | 543 | QPushButton:pressed { 544 | background-color: #3daee9; 545 | padding-top: -15px; 546 | padding-bottom: -17px; 547 | } 548 | 549 | QComboBox { 550 | selection-background-color: #3daee9; 551 | border-style: solid; 552 | border: 1px solid #76797C; 553 | border-radius: 2px; 554 | padding: 5px; 555 | } 556 | 557 | QPushButton:checked { 558 | background-color: #76797C; 559 | border-color: #6A6969; 560 | } 561 | 562 | QComboBox:hover, 563 | QPushButton:hover, 564 | QAbstractSpinBox:hover, 565 | QLineEdit:hover, 566 | QTextEdit:hover, 567 | QPlainTextEdit:hover, 568 | QAbstractView:hover, 569 | QTreeView:hover { 570 | border: 1px solid #3daee9; 571 | color: #eff0f1; 572 | } 573 | 574 | QComboBox:on { 575 | padding-top: 3px; 576 | padding-left: 4px; 577 | selection-background-color: #4a4a4a; 578 | } 579 | 580 | QComboBox QAbstractItemView { 581 | background-color: #232629; 582 | border-radius: 2px; 583 | border: 1px solid #76797C; 584 | selection-background-color: #18465d; 585 | } 586 | 587 | QComboBox::drop-down { 588 | subcontrol-origin: padding; 589 | subcontrol-position: top right; 590 | width: 15px; 591 | border-left-width: 0px; 592 | border-left-color: darkgray; 593 | border-left-style: solid; 594 | border-top-right-radius: 3px; 595 | border-bottom-right-radius: 3px; 596 | } 597 | 598 | QComboBox::down-arrow { 599 | image: url(:/qss_icons/rc/down_arrow_disabled.png); 600 | } 601 | 602 | QComboBox::down-arrow:on, 603 | QComboBox::down-arrow:hover, 604 | QComboBox::down-arrow:focus { 605 | image: url(:/qss_icons/rc/down_arrow.png); 606 | } 607 | 608 | QAbstractSpinBox { 609 | padding: 5px; 610 | border: 1px solid #76797C; 611 | background-color: #232629; 612 | color: #eff0f1; 613 | border-radius: 2px; 614 | min-width: 75px; 615 | } 616 | 617 | QAbstractSpinBox:up-button { 618 | background-color: transparent; 619 | subcontrol-origin: border; 620 | subcontrol-position: center right; 621 | } 622 | 623 | QAbstractSpinBox:down-button { 624 | background-color: transparent; 625 | subcontrol-origin: border; 626 | subcontrol-position: center left; 627 | } 628 | 629 | QAbstractSpinBox::up-arrow, 630 | QAbstractSpinBox::up-arrow:disabled, 631 | QAbstractSpinBox::up-arrow:off { 632 | image: url(:/qss_icons/rc/up_arrow_disabled.png); 633 | width: 10px; 634 | height: 10px; 635 | } 636 | 637 | QAbstractSpinBox::up-arrow:hover { 638 | image: url(:/qss_icons/rc/up_arrow.png); 639 | } 640 | 641 | QAbstractSpinBox::down-arrow, 642 | QAbstractSpinBox::down-arrow:disabled, 643 | QAbstractSpinBox::down-arrow:off { 644 | image: url(:/qss_icons/rc/down_arrow_disabled.png); 645 | width: 10px; 646 | height: 10px; 647 | } 648 | 649 | QAbstractSpinBox::down-arrow:hover { 650 | image: url(:/qss_icons/rc/down_arrow.png); 651 | } 652 | 653 | QLabel { 654 | border: 0px solid black; 655 | } 656 | 657 | QTabWidget { 658 | border: 0px transparent black; 659 | } 660 | 661 | QTabWidget::pane { 662 | border: 1px solid #76797C; 663 | padding: 5px; 664 | margin: 0px; 665 | } 666 | 667 | QTabWidget::tab-bar { 668 | /* left: 5px; move to the right by 5px */ 669 | } 670 | 671 | QTabBar { 672 | qproperty-drawBase: 0; 673 | border-radius: 3px; 674 | } 675 | 676 | QTabBar:focus { 677 | border: 0px transparent black; 678 | } 679 | 680 | QTabBar::close-button { 681 | image: url(:/qss_icons/rc/close.png); 682 | background: transparent; 683 | } 684 | 685 | QTabBar::close-button:hover { 686 | image: url(:/qss_icons/rc/close-hover.png); 687 | background: transparent; 688 | } 689 | 690 | QTabBar::close-button:pressed { 691 | image: url(:/qss_icons/rc/close-pressed.png); 692 | background: transparent; 693 | } 694 | 695 | 696 | /* TOP TABS */ 697 | 698 | QTabBar::tab:top { 699 | color: #eff0f1; 700 | border: 1px solid #76797C; 701 | border-bottom: 1px transparent black; 702 | background-color: #31363b; 703 | padding: 5px; 704 | min-width: 50px; 705 | border-top-left-radius: 2px; 706 | border-top-right-radius: 2px; 707 | } 708 | 709 | QTabBar::tab:top:selected { 710 | color: #eff0f1; 711 | background-color: #54575B; 712 | border: 1px solid #76797C; 713 | border-bottom: 2px solid #3daee9; 714 | border-top-left-radius: 2px; 715 | border-top-right-radius: 2px; 716 | } 717 | 718 | QTabBar::tab:top:!selected:hover { 719 | background-color: #3daee9; 720 | } 721 | 722 | 723 | /* BOTTOM TABS */ 724 | 725 | QTabBar::tab:bottom { 726 | color: #eff0f1; 727 | border: 1px solid #76797C; 728 | border-top: 1px transparent black; 729 | background-color: #31363b; 730 | padding: 5px; 731 | border-bottom-left-radius: 2px; 732 | border-bottom-right-radius: 2px; 733 | min-width: 50px; 734 | } 735 | 736 | QTabBar::tab:bottom:selected { 737 | color: #eff0f1; 738 | background-color: #54575B; 739 | border: 1px solid #76797C; 740 | border-top: 2px solid #3daee9; 741 | border-bottom-left-radius: 2px; 742 | border-bottom-right-radius: 2px; 743 | } 744 | 745 | QTabBar::tab:bottom:!selected:hover { 746 | background-color: #3daee9; 747 | } 748 | 749 | 750 | /* LEFT TABS */ 751 | 752 | QTabBar::tab:left { 753 | color: #eff0f1; 754 | border: 1px solid #76797C; 755 | border-left: 1px transparent black; 756 | background-color: #31363b; 757 | padding: 5px; 758 | border-top-right-radius: 2px; 759 | border-bottom-right-radius: 2px; 760 | min-height: 50px; 761 | } 762 | 763 | QTabBar::tab:left:selected { 764 | color: #eff0f1; 765 | background-color: #54575B; 766 | border: 1px solid #76797C; 767 | border-left: 2px solid #3daee9; 768 | border-top-right-radius: 2px; 769 | border-bottom-right-radius: 2px; 770 | } 771 | 772 | QTabBar::tab:left:!selected:hover { 773 | background-color: #3daee9; 774 | } 775 | 776 | 777 | /* RIGHT TABS */ 778 | 779 | QTabBar::tab:right { 780 | color: #eff0f1; 781 | border: 1px solid #76797C; 782 | border-right: 1px transparent black; 783 | background-color: #31363b; 784 | padding: 5px; 785 | border-top-left-radius: 2px; 786 | border-bottom-left-radius: 2px; 787 | min-height: 50px; 788 | } 789 | 790 | QTabBar::tab:right:selected { 791 | color: #eff0f1; 792 | background-color: #54575B; 793 | border: 1px solid #76797C; 794 | border-right: 2px solid #3daee9; 795 | border-top-left-radius: 2px; 796 | border-bottom-left-radius: 2px; 797 | } 798 | 799 | QTabBar::tab:right:!selected:hover { 800 | background-color: #3daee9; 801 | } 802 | 803 | QTabBar QToolButton::right-arrow:enabled { 804 | image: url(:/qss_icons/rc/right_arrow.png); 805 | } 806 | 807 | QTabBar QToolButton::left-arrow:enabled { 808 | image: url(:/qss_icons/rc/left_arrow.png); 809 | } 810 | 811 | QTabBar QToolButton::right-arrow:disabled { 812 | image: url(:/qss_icons/rc/right_arrow_disabled.png); 813 | } 814 | 815 | QTabBar QToolButton::left-arrow:disabled { 816 | image: url(:/qss_icons/rc/left_arrow_disabled.png); 817 | } 818 | 819 | QDockWidget { 820 | background: #31363b; 821 | border: 1px solid #403F3F; 822 | titlebar-close-icon: url(:/qss_icons/rc/close.png); 823 | titlebar-normal-icon: url(:/qss_icons/rc/undock.png); 824 | } 825 | 826 | QDockWidget::close-button, 827 | QDockWidget::float-button { 828 | border: 1px solid transparent; 829 | border-radius: 2px; 830 | background: transparent; 831 | } 832 | 833 | QDockWidget::close-button:hover, 834 | QDockWidget::float-button:hover { 835 | background: rgba(255, 255, 255, 10); 836 | } 837 | 838 | QDockWidget::close-button:pressed, 839 | QDockWidget::float-button:pressed { 840 | padding: 1px -1px -1px 1px; 841 | background: rgba(255, 255, 255, 10); 842 | } 843 | 844 | QTreeView, 845 | QListView { 846 | border: 1px solid #76797C; 847 | background-color: #232629; 848 | } 849 | 850 | QTreeView:branch:selected, 851 | QTreeView:branch:hover { 852 | background: url(:/qss_icons/rc/transparent.png); 853 | } 854 | 855 | QTreeView::branch:has-siblings:!adjoins-item { 856 | border-image: url(:/qss_icons/rc/transparent.png); 857 | } 858 | 859 | QTreeView::branch:has-siblings:adjoins-item { 860 | border-image: url(:/qss_icons/rc/transparent.png); 861 | } 862 | 863 | QTreeView::branch:!has-children:!has-siblings:adjoins-item { 864 | border-image: url(:/qss_icons/rc/transparent.png); 865 | } 866 | 867 | QTreeView::branch:has-children:!has-siblings:closed, 868 | QTreeView::branch:closed:has-children:has-siblings { 869 | image: url(:/qss_icons/rc/branch_closed.png); 870 | } 871 | 872 | QTreeView::branch:open:has-children:!has-siblings, 873 | QTreeView::branch:open:has-children:has-siblings { 874 | image: url(:/qss_icons/rc/branch_open.png); 875 | } 876 | 877 | QTreeView::branch:has-children:!has-siblings:closed:hover, 878 | QTreeView::branch:closed:has-children:has-siblings:hover { 879 | image: url(:/qss_icons/rc/branch_closed-on.png); 880 | } 881 | 882 | QTreeView::branch:open:has-children:!has-siblings:hover, 883 | QTreeView::branch:open:has-children:has-siblings:hover { 884 | image: url(:/qss_icons/rc/branch_open-on.png); 885 | } 886 | 887 | QListView::item:!selected:hover, 888 | QTreeView::item:!selected:hover { 889 | background: #18465d; 890 | outline: 0; 891 | color: #eff0f1 892 | } 893 | 894 | QListView::item:selected:hover, 895 | QTreeView::item:selected:hover { 896 | background: #287399; 897 | color: #eff0f1; 898 | } 899 | 900 | QTreeView::indicator:checked, 901 | QListView::indicator:checked { 902 | image: url(:/qss_icons/rc/checkbox_checked.png); 903 | } 904 | 905 | QTreeView::indicator:unchecked, 906 | QListView::indicator:unchecked { 907 | image: url(:/qss_icons/rc/checkbox_unchecked.png); 908 | } 909 | 910 | QTreeView::indicator:indeterminate, 911 | QListView::indicator:indeterminate { 912 | image: url(:/qss_icons/rc/checkbox_indeterminate.png); 913 | } 914 | 915 | QTreeView::indicator:checked:hover, 916 | QTreeView::indicator:checked:focus, 917 | QTreeView::indicator:checked:pressed, 918 | QListView::indicator:checked:hover, 919 | QListView::indicator:checked:focus, 920 | QListView::indicator:checked:pressed { 921 | image: url(:/qss_icons/rc/checkbox_checked_focus.png); 922 | } 923 | 924 | QTreeView::indicator:unchecked:hover, 925 | QTreeView::indicator:unchecked:focus, 926 | QTreeView::indicator:unchecked:pressed, 927 | QListView::indicator:unchecked:hover, 928 | QListView::indicator:unchecked:focus, 929 | QListView::indicator:unchecked:pressed { 930 | image: url(:/qss_icons/rc/checkbox_unchecked_focus.png); 931 | } 932 | 933 | QTreeView::indicator:indeterminate:hover, 934 | QTreeView::indicator:indeterminate:focus, 935 | QTreeView::indicator:indeterminate:pressed, 936 | QListView::indicator:indeterminate:hover, 937 | QListView::indicator:indeterminate:focus, 938 | QListView::indicator:indeterminate:pressed { 939 | image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png); 940 | } 941 | 942 | QSlider::groove:horizontal { 943 | border: 1px solid #565a5e; 944 | height: 4px; 945 | background: #565a5e; 946 | margin: 0px; 947 | border-radius: 2px; 948 | } 949 | 950 | QSlider::handle:horizontal { 951 | background: #232629; 952 | border: 1px solid #565a5e; 953 | width: 16px; 954 | height: 16px; 955 | margin: -8px 0; 956 | border-radius: 9px; 957 | } 958 | 959 | QSlider::groove:vertical { 960 | border: 1px solid #565a5e; 961 | width: 4px; 962 | background: #565a5e; 963 | margin: 0px; 964 | border-radius: 3px; 965 | } 966 | 967 | QSlider::handle:vertical { 968 | background: #232629; 969 | border: 1px solid #565a5e; 970 | width: 16px; 971 | height: 16px; 972 | margin: 0 -8px; 973 | border-radius: 9px; 974 | } 975 | 976 | QToolButton { 977 | background-color: transparent; 978 | border: 1px transparent #76797C; 979 | border-radius: 2px; 980 | margin: 3px; 981 | padding: 5px; 982 | } 983 | 984 | QToolButton[popupMode="1"] { 985 | /* only for MenuButtonPopup */ 986 | padding-right: 20px; 987 | /* make way for the popup button */ 988 | border: 1px #76797C; 989 | border-radius: 5px; 990 | } 991 | 992 | QToolButton[popupMode="2"] { 993 | /* only for InstantPopup */ 994 | padding-right: 10px; 995 | /* make way for the popup button */ 996 | border: 1px #76797C; 997 | } 998 | 999 | QToolButton:hover, 1000 | QToolButton::menu-button:hover { 1001 | background-color: transparent; 1002 | border: 1px solid #3daee9; 1003 | padding: 5px; 1004 | } 1005 | 1006 | QToolButton:checked, 1007 | QToolButton:pressed, 1008 | QToolButton::menu-button:pressed { 1009 | background-color: #3daee9; 1010 | border: 1px solid #3daee9; 1011 | padding: 5px; 1012 | } 1013 | 1014 | 1015 | /* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */ 1016 | 1017 | QToolButton::menu-indicator { 1018 | image: url(:/qss_icons/rc/down_arrow.png); 1019 | top: -7px; 1020 | left: -2px; 1021 | /* shift it a bit */ 1022 | } 1023 | 1024 | 1025 | /* the subcontrols below are used only in the MenuButtonPopup mode */ 1026 | 1027 | QToolButton::menu-button { 1028 | border: 1px transparent #76797C; 1029 | border-top-right-radius: 6px; 1030 | border-bottom-right-radius: 6px; 1031 | /* 16px width + 4px for border = 20px allocated above */ 1032 | width: 16px; 1033 | outline: none; 1034 | } 1035 | 1036 | QToolButton::menu-arrow { 1037 | image: url(:/qss_icons/rc/down_arrow.png); 1038 | } 1039 | 1040 | QToolButton::menu-arrow:open { 1041 | border: 1px solid #76797C; 1042 | } 1043 | 1044 | QPushButton::menu-indicator { 1045 | subcontrol-origin: padding; 1046 | subcontrol-position: bottom right; 1047 | left: 8px; 1048 | } 1049 | 1050 | QTableView { 1051 | border: 1px solid #76797C; 1052 | gridline-color: #383f46; 1053 | background-color: #232629; 1054 | } 1055 | 1056 | QTableView, 1057 | QHeaderView { 1058 | border-radius: 0px; 1059 | } 1060 | 1061 | QTableView::item:pressed, 1062 | QListView::item:pressed, 1063 | QTreeView::item:pressed { 1064 | background: #18465d; 1065 | color: #eff0f1; 1066 | } 1067 | 1068 | QTableView::item:selected:active, 1069 | QTreeView::item:selected:active, 1070 | QListView::item:selected:active { 1071 | background: #287399; 1072 | color: #eff0f1; 1073 | } 1074 | 1075 | QHeaderView { 1076 | background-color: #31363b; 1077 | border: 1px transparent; 1078 | border-radius: 0px; 1079 | margin: 0px; 1080 | padding: 0px; 1081 | } 1082 | 1083 | QHeaderView::section { 1084 | background-color: #31363b; 1085 | color: #eff0f1; 1086 | padding: 5px; 1087 | border: 1px solid #76797C; 1088 | border-radius: 0px; 1089 | text-align: center; 1090 | } 1091 | 1092 | QHeaderView::section::vertical::first, 1093 | QHeaderView::section::vertical::only-one { 1094 | border-top: 1px solid #76797C; 1095 | } 1096 | 1097 | QHeaderView::section::vertical { 1098 | border-top: transparent; 1099 | } 1100 | 1101 | QHeaderView::section::horizontal::first, 1102 | QHeaderView::section::horizontal::only-one { 1103 | border-left: 1px solid #76797C; 1104 | } 1105 | 1106 | QHeaderView::section::horizontal { 1107 | border-left: transparent; 1108 | } 1109 | 1110 | QHeaderView::section:checked { 1111 | color: white; 1112 | background-color: #334e5e; 1113 | } 1114 | 1115 | 1116 | /* style the sort indicator */ 1117 | 1118 | QHeaderView::down-arrow { 1119 | image: url(:/qss_icons/rc/down_arrow.png); 1120 | } 1121 | 1122 | QHeaderView::up-arrow { 1123 | image: url(:/qss_icons/rc/up_arrow.png); 1124 | } 1125 | 1126 | QTableCornerButton::section { 1127 | background-color: #31363b; 1128 | border: 1px transparent #76797C; 1129 | border-radius: 0px; 1130 | } 1131 | 1132 | QToolBox { 1133 | padding: 5px; 1134 | border: 1px transparent black; 1135 | } 1136 | 1137 | QToolBox::tab { 1138 | color: #eff0f1; 1139 | background-color: #31363b; 1140 | border: 1px solid #76797C; 1141 | border-bottom: 1px transparent #31363b; 1142 | border-top-left-radius: 5px; 1143 | border-top-right-radius: 5px; 1144 | } 1145 | 1146 | QToolBox::tab:selected { 1147 | /* italicize selected tabs */ 1148 | font: italic; 1149 | background-color: #31363b; 1150 | border-color: #3daee9; 1151 | } 1152 | 1153 | QStatusBar::item { 1154 | border: 0px transparent dark; 1155 | } 1156 | 1157 | QFrame[height="3"], 1158 | QFrame[width="3"] { 1159 | background-color: #76797C; 1160 | } 1161 | 1162 | QSplitter::handle { 1163 | border: 1px dashed #76797C; 1164 | } 1165 | 1166 | QSplitter::handle:hover { 1167 | background-color: #787876; 1168 | border: 1px solid #76797C; 1169 | } 1170 | 1171 | QSplitter::handle:horizontal { 1172 | width: 1px; 1173 | } 1174 | 1175 | QSplitter::handle:vertical { 1176 | height: 1px; 1177 | } 1178 | 1179 | QProgressBar { 1180 | border: 1px solid #76797C; 1181 | border-radius: 5px; 1182 | text-align: center; 1183 | } 1184 | 1185 | QProgressBar::chunk { 1186 | background-color: #05B8CC; 1187 | } 1188 | 1189 | QDateEdit { 1190 | selection-background-color: #3daee9; 1191 | border-style: solid; 1192 | border: 1px solid #3375A3; 1193 | border-radius: 2px; 1194 | padding: 1px; 1195 | min-width: 75px; 1196 | } 1197 | 1198 | QDateEdit:on { 1199 | padding-top: 3px; 1200 | padding-left: 4px; 1201 | selection-background-color: #4a4a4a; 1202 | } 1203 | 1204 | QDateEdit QAbstractItemView { 1205 | background-color: #232629; 1206 | border-radius: 2px; 1207 | border: 1px solid #3375A3; 1208 | selection-background-color: #3daee9; 1209 | } 1210 | 1211 | QDateEdit::drop-down { 1212 | subcontrol-origin: padding; 1213 | subcontrol-position: top right; 1214 | width: 15px; 1215 | border-left-width: 0px; 1216 | border-left-color: darkgray; 1217 | border-left-style: solid; 1218 | border-top-right-radius: 3px; 1219 | border-bottom-right-radius: 3px; 1220 | } 1221 | 1222 | QDateEdit::down-arrow { 1223 | image: url(:/qss_icons/rc/down_arrow_disabled.png); 1224 | } 1225 | 1226 | QDateEdit::down-arrow:on, 1227 | QDateEdit::down-arrow:hover, 1228 | QDateEdit::down-arrow:focus { 1229 | image: url(:/qss_icons/rc/down_arrow.png); 1230 | } --------------------------------------------------------------------------------