class QtStyleTools: """""" extra_colors = {} # ---------------------------------------------------------------------- def set_extra_colors(self, extra): """""" self.extra_colors = extra # ---------------------------------------------------------------------- def add_menu_theme(self, parent, menu): """""" for theme in ['default'] + list_themes(): action = QAction(parent) action.setText(theme) action.triggered.connect( self._wrapper(parent, theme, self.extra_colors, self.update_buttons)) menu.addAction(action) # ---------------------------------------------------------------------- def _wrapper(self, parent, theme, extra, callable_): """""" def iner(): self._apply_theme(parent, theme, extra, callable_) return iner # ---------------------------------------------------------------------- def _apply_theme(self, parent, theme, extra={}, callable_=None): """""" self.apply_stylesheet(parent, theme=theme, invert_secondary=theme.startswith('light'), extra=extra, callable_=callable_) # ---------------------------------------------------------------------- def apply_stylesheet(self, parent, theme, invert_secondary=False, extra={}, callable_=None): """""" if theme == 'default': parent.setStyleSheet('') return apply_stylesheet(parent, theme=theme, invert_secondary=invert_secondary, extra=extra) if callable_: callable_() # ---------------------------------------------------------------------- def update_buttons(self): """""" if not hasattr(self, 'colors'): return theme = { color_: os.environ[f'QTMATERIAL_{color_.upper()}'] for color_ in self.colors } if 'light' in os.environ['QTMATERIAL_THEME']: self.dock_theme.checkBox_ligh_theme.setChecked(True) elif 'dark' in os.environ['QTMATERIAL_THEME']: self.dock_theme.checkBox_ligh_theme.setChecked(False) if self.dock_theme.checkBox_ligh_theme.isChecked(): theme['secondaryColor'], theme['secondaryLightColor'], theme[ 'secondaryDarkColor'] = theme['secondaryColor'], theme[ 'secondaryDarkColor'], theme['secondaryLightColor'] for color_ in self.colors: button = getattr(self.dock_theme, f'pushButton_{color_}') color = theme[color_] if self.get_color(color).getHsv()[2] < 128: text_color = '#ffffff' else: text_color = '#000000' button.setStyleSheet(f""" *{{ background-color: {color}; color: {text_color}; border: none; }}""") self.custom_colors[color_] = color # ---------------------------------------------------------------------- def get_color(self, color): """""" return QColor(*[int(color[s:s + 2], 16) for s in range(1, 6, 2)]) # ---------------------------------------------------------------------- def update_theme(self, parent): """""" with open('my_theme.xml', 'w') as file: file.write(""" <resources> <color name="primaryColor">{primaryColor}</color> <color name="primaryLightColor">{primaryLightColor}</color> <color name="secondaryColor">{secondaryColor}</color> <color name="secondaryLightColor">{secondaryLightColor}</color> <color name="secondaryDarkColor">{secondaryDarkColor}</color> <color name="primaryTextColor">{primaryTextColor}</color> <color name="secondaryTextColor">{secondaryTextColor}</color> </resources> """.format(**self.custom_colors)) light = self.dock_theme.checkBox_ligh_theme.isChecked() self.apply_stylesheet(parent, 'my_theme.xml', invert_secondary=light, extra=self.extra_colors, callable_=self.update_buttons) # ---------------------------------------------------------------------- def set_color(self, parent, button_): """""" def iner(): initial = self.get_color(self.custom_colors[button_]) color_dialog = QColorDialog(parent=parent) color_dialog.setCurrentColor(initial) done = color_dialog.exec_() color_ = color_dialog.currentColor() if done and color_.isValid(): color = '#' + ''.join( [hex(v)[2:].ljust(2, '0') for v in color_.toTuple()[:3]]) self.custom_colors[button_] = color self.update_theme(parent) return iner # ---------------------------------------------------------------------- def show_dock_theme(self, parent): """""" self.colors = [ 'primaryColor', 'primaryLightColor', 'secondaryColor', 'secondaryLightColor', 'secondaryDarkColor', 'primaryTextColor', 'secondaryTextColor' ] self.custom_colors = { v: os.environ[f'QTMATERIAL_{v.upper()}'] for v in self.colors } if 'PySide2' in sys.modules or 'PySide6' in sys.modules: self.dock_theme = QUiLoader().load( os.path.join(os.path.dirname(__file__), 'dock_theme.ui')) elif 'PyQt5' in sys.modules: self.dock_theme = uic.loadUi( os.path.join(os.path.dirname(__file__), 'dock_theme.ui')) parent.addDockWidget(Qt.LeftDockWidgetArea, self.dock_theme) self.dock_theme.setFloating(True) self.update_buttons() self.dock_theme.checkBox_ligh_theme.clicked.connect( lambda: self.update_theme(self.main)) for color in self.colors: button = getattr(self.dock_theme, f'pushButton_{color}') button.clicked.connect(self.set_color(parent, color))
class QtStyleTools: """""" extra_values = {} # ---------------------------------------------------------------------- @deprecated('set_extra') def set_extra_colors(self, extra): """""" self.extra_values = extra # ---------------------------------------------------------------------- def set_extra(self, extra): """""" self.extra_values = extra # ---------------------------------------------------------------------- def add_menu_theme(self, parent, menu): """""" self.menu_theme_ = menu action_group = QActionGroup(menu) try: action_group.setExclusive(True) except: action_group.exclusive = True for i, theme in enumerate(['default'] + list_themes()): action = QAction(parent) # action.triggered.connect(self._wrapper(parent, theme, self.extra_values, self.update_buttons)) action.triggered.connect(lambda: self.update_theme_event(parent)) try: action.setText(theme) action.setCheckable(True) action.setChecked(not bool(i)) action.setActionGroup(action_group) menu.addAction(action) action_group.addAction(action) except: # snake_case, true_property action.text = theme action.checkable = True action.checked = not bool(i) action.action_group = action_group menu.add_action(action) action_group.add_action(action) # ---------------------------------------------------------------------- def add_menu_density(self, parent, menu): """""" self.menu_density_ = menu action_group = QActionGroup(menu) try: action_group.setExclusive(True) except: action_group.exclusive = True for density in map(str, range(-3, 4)): action = QAction(parent) # action.triggered.connect(self._wrapper(parent, density, self.extra_values, self.update_buttons)) action.triggered.connect(lambda: self.update_theme_event(parent)) try: action.setText(density) action.setCheckable(True) action.setChecked(density == '0') action.setActionGroup(action_group) menu.addAction(action) action_group.addAction(action) except: # snake_case, true_property action.text = density action.checkable = True action.checked = density == '0' action.action_group = action_group menu.add_action(action) action_group.add_action(action) # menu.add_action(action_group) # # ---------------------------------------------------------------------- # def _wrapper(self, parent, theme, extra, callable_): # """""" # def iner(): # self._apply_theme(parent, theme, extra, callable_) # return iner # # ---------------------------------------------------------------------- # def _apply_theme(self, parent, theme, extra={}, callable_=None): # """""" # self.apply_stylesheet(parent, theme=theme, invert_secondary=theme.startswith( # 'light'), extra=extra, callable_=callable_) # ---------------------------------------------------------------------- def apply_stylesheet(self, parent, theme, invert_secondary=False, extra={}, callable_=None): """""" if theme == 'default': parent.setStyleSheet('') return apply_stylesheet(parent, theme=theme, invert_secondary=invert_secondary, extra=extra) if callable_: callable_() # ---------------------------------------------------------------------- def update_theme_event(self, parent): """""" try: density = [action.text() for action in self.menu_density_.actions( ) if action.isChecked()][0] theme = [action.text() for action in self.menu_theme_.actions() if action.isChecked()][0] except: density = [ action.text for action in self.menu_density_.actions() if action.checked][0] theme = [action.text for action in self.menu_theme_.actions() if action.checked][0] self.extra_values['density_scale'] = density self.apply_stylesheet(parent, theme=theme, invert_secondary=theme.startswith( 'light'), extra=self.extra_values, callable_=self.update_buttons) # ---------------------------------------------------------------------- def update_buttons(self): """""" if not hasattr(self, 'colors'): return theme = {color_: os.environ[f'QTMATERIAL_{color_.upper()}'] for color_ in self.colors} if 'light' in os.environ['QTMATERIAL_THEME']: self.dock_theme.checkBox_ligh_theme.setChecked(True) elif 'dark' in os.environ['QTMATERIAL_THEME']: self.dock_theme.checkBox_ligh_theme.setChecked(False) try: if self.dock_theme.checkBox_ligh_theme.isChecked(): theme['secondaryColor'], theme['secondaryLightColor'], theme['secondaryDarkColor'] = theme[ 'secondaryColor'], theme['secondaryDarkColor'], theme['secondaryLightColor'] except: # snake_case, true_property if self.dock_theme.checkBox_ligh_theme.checked: theme['secondaryColor'], theme['secondaryLightColor'], theme['secondaryDarkColor'] = theme[ 'secondaryColor'], theme['secondaryDarkColor'], theme['secondaryLightColor'] for color_ in self.colors: button = getattr(self.dock_theme, f'pushButton_{color_}') color = theme[color_] try: if self.get_color(color).getHsv()[2] < 128: text_color = '#ffffff' else: text_color = '#000000' except: # snake_case, true_property if self.get_color(color).get_hsv()[2] < 128: text_color = '#ffffff' else: text_color = '#000000' button.setStyleSheet(f""" *{{ background-color: {color}; color: {text_color}; border: none; }}""") self.custom_colors[color_] = color # ---------------------------------------------------------------------- def get_color(self, color): """""" return QColor(*[int(color[s:s + 2], 16) for s in range(1, 6, 2)]) # ---------------------------------------------------------------------- def update_theme(self, parent): """""" with open('my_theme.xml', 'w') as file: file.write(""" <resources> <color name="primaryColor">{primaryColor}</color> <color name="primaryLightColor">{primaryLightColor}</color> <color name="secondaryColor">{secondaryColor}</color> <color name="secondaryLightColor">{secondaryLightColor}</color> <color name="secondaryDarkColor">{secondaryDarkColor}</color> <color name="primaryTextColor">{primaryTextColor}</color> <color name="secondaryTextColor">{secondaryTextColor}</color> </resources> """.format(**self.custom_colors)) try: light = self.dock_theme.checkBox_ligh_theme.isChecked() except: # snake_case, true_property light = self.dock_theme.checkBox_ligh_theme.checked self.apply_stylesheet(parent, 'my_theme.xml', invert_secondary=light, extra=self.extra_values, callable_=self.update_buttons) # ---------------------------------------------------------------------- def set_color(self, parent, button_): """""" def iner(): initial = self.get_color(self.custom_colors[button_]) color_dialog = QColorDialog(parent=parent) try: color_dialog.setCurrentColor(initial) except: # snake_case, true_property color_dialog.current_color = initial done = color_dialog.exec_() try: color_ = color_dialog.currentColor() except: # snake_case, true_property color_ = color_dialog.current_color try: if done and color_.isValid(): rgb_255 = [color_.red(), color_.green(), color_.blue()] color = '#' + ''.join([hex(v)[2:].ljust(2, '0') for v in rgb_255]) self.custom_colors[button_] = color self.update_theme(parent) except: # snake_case, true_property if done and color_.is_valid(): rgb_255 = [color_.red(), color_.green(), color_.blue()] color = '#' + ''.join([hex(v)[2:].ljust(2, '0') for v in rgb_255]) self.custom_colors[button_] = color self.update_theme(parent) return iner # ---------------------------------------------------------------------- def show_dock_theme(self, parent): """""" self.colors = ['primaryColor', 'primaryLightColor', 'secondaryColor', 'secondaryLightColor', 'secondaryDarkColor', 'primaryTextColor', 'secondaryTextColor'] self.custom_colors = { v: os.environ[f'QTMATERIAL_{v.upper()}'] for v in self.colors} if 'PySide2' in sys.modules or 'PySide6' in sys.modules: self.dock_theme = QUiLoader().load(os.path.join( os.path.dirname(__file__), 'dock_theme.ui')) elif 'PyQt5' in sys.modules or 'PyQt6' in sys.modules: self.dock_theme = uic.loadUi(os.path.join( os.path.dirname(__file__), 'dock_theme.ui')) try: parent.addDockWidget( Qt.DockWidgetArea.LeftDockWidgetArea, self.dock_theme) self.dock_theme.setFloating(True) except: # snake_case, true_property parent.add_dock_widget( Qt.DockWidgetArea.LeftDockWidgetArea, self.dock_theme) self.dock_theme.floating = True self.update_buttons() self.dock_theme.checkBox_ligh_theme.clicked.connect( lambda: self.update_theme(self.main)) for color in self.colors: button = getattr(self.dock_theme, f'pushButton_{color}') button.clicked.connect(self.set_color(parent, color))