提交 506304e3 编写于 作者: 之一Yo's avatar 之一Yo

添加更多 QtDesigner 插件

上级 ecb7bba1
# coding: utf-8
from PyQt5.QtCore import Qt
from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin
from qfluentwidgets import (PushButton, PrimaryPushButton, SplitPushButton, DropDownPushButton,
from qfluentwidgets import (PrimaryPushButton, SplitPushButton, DropDownPushButton,
ToolButton, SplitToolButton, DropDownToolButton, FluentIcon, ToggleButton,
SwitchButton, RadioButton, CheckBox, HyperlinkButton, Slider, ComboBox, IconWidget)
SwitchButton, RadioButton, CheckBox, HyperlinkButton, Slider, ComboBox, IconWidget,
EditableComboBox, PixmapLabel, PushButton)
from plugin_base import PluginBase
from task_menu_fatcory import EditTextTaskMenuFactory
class BasicInputPlugin(PluginBase):
......@@ -41,6 +43,19 @@ class ComboBoxPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
return "ComboBox"
class EditableComboBoxPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
""" Editable box plugin """
def createWidget(self, parent):
return EditableComboBox(parent)
def icon(self):
return super().icon('ComboBox')
def name(self):
return "EditableComboBox"
class HyperlinkButtonPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
""" Hyperlink button plugin """
......@@ -92,7 +107,7 @@ class DropDownPushButtonPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
def name(self):
return "DropDownPushButton"
@EditTextTaskMenuFactory.register
class SplitPushButtonPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
""" Split push button plugin """
......@@ -145,6 +160,7 @@ class SplitToolButtonPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
return "SplitToolButton"
@EditTextTaskMenuFactory.register
class SwitchButtonPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
""" Switch button plugin """
......@@ -212,3 +228,16 @@ class IconWidgetPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
def name(self):
return "IconWidget"
class PixmapLabelPlugin(BasicInputPlugin, QPyDesignerCustomWidgetPlugin):
""" Pixmap label plugin """
def createWidget(self, parent):
return PixmapLabel(parent)
def icon(self):
return super().icon('Image')
def name(self):
return "PixmapLabel"
# coding: utf-8
from PyQt5.QtCore import Qt
from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin
from qfluentwidgets import ScrollArea, SmoothScrollArea, SingleDirectionScrollArea, OpacityAniStackedWidget, PopUpAniStackedWidget
from qframelesswindow import FramelessMainWindow, FramelessWindow
from plugin_base import PluginBase
class ContainerPlugin(PluginBase):
def group(self):
return super().group() + ' (Container)'
def isContainer(self):
return True
class FramelessMainWindowPlugin(ContainerPlugin, QPyDesignerCustomWidgetPlugin):
""" Frameless main window plugin """
def createWidget(self, parent):
return FramelessMainWindow(parent)
def icon(self):
return super().icon("TitleBar")
def name(self):
return "FramelessMainWindow"
class FramelessWindowPlugin(ContainerPlugin, QPyDesignerCustomWidgetPlugin):
""" Frameless window plugin """
def createWidget(self, parent):
return FramelessWindow(parent)
def icon(self):
return super().icon("TitleBar")
def name(self):
return "FramelessWindow"
class ScrollAreaPlugin(ContainerPlugin, QPyDesignerCustomWidgetPlugin):
""" Scroll area plugin """
def createWidget(self, parent):
return ScrollArea(parent)
def icon(self):
return super().icon("ScrollViewer")
def name(self):
return "ScrollArea"
def toolTip(self):
return "Smooth scroll area"
class SmoothScrollAreaPlugin(ContainerPlugin, QPyDesignerCustomWidgetPlugin):
""" Smooth scroll area plugin """
def createWidget(self, parent):
return SmoothScrollArea(parent)
def icon(self):
return super().icon("ScrollViewer")
def name(self):
return "SmoothScrollArea"
class SingleDirectionScrollAreaPlugin(ContainerPlugin, QPyDesignerCustomWidgetPlugin):
""" Single direction scroll area plugin """
def createWidget(self, parent):
return SingleDirectionScrollArea(parent)
def icon(self):
return super().icon("ScrollViewer")
def name(self):
return "SingleDirectionScrollArea"
class OpacityAniStackedWidgetPlugin(ContainerPlugin, QPyDesignerCustomWidgetPlugin):
""" opacity ani stacked widget plugin """
def createWidget(self, parent):
return OpacityAniStackedWidget(parent)
def icon(self):
return super().icon("StackPanel")
def name(self):
return "OpacityAniStackedWidget"
class PopUpAniStackedWidgetPlugin(ContainerPlugin, QPyDesignerCustomWidgetPlugin):
""" pop up ani stacked widget plugin """
def createWidget(self, parent):
return PopUpAniStackedWidget(parent)
def icon(self):
return super().icon("StackPanel")
def name(self):
return "PopUpAniStackedWidget"
# coding: utf-8
from PyQt5.QtCore import Qt
from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin
from qfluentwidgets import NavigationInterface, NavigationPanel
from plugin_base import PluginBase
class NavigationPlugin(PluginBase):
def group(self):
return super().group() + ' (Navigation)'
class NavigationInterfacePlugin(NavigationPlugin, QPyDesignerCustomWidgetPlugin):
""" Navigation interface plugin """
def createWidget(self, parent):
return NavigationInterface(parent, True, True)
def icon(self):
return super().icon("NavigationView")
def name(self):
return "NavigationInterface"
class NavigationPanelPlugin(NavigationPlugin, QPyDesignerCustomWidgetPlugin):
""" Navigation panel plugin """
def createWidget(self, parent):
return NavigationPanel(parent)
def icon(self):
return super().icon("NavigationView")
def name(self):
return "NavigationPanel"
......@@ -2,19 +2,30 @@
import re
from PyQt5.QtGui import QIcon
from PyQt5.QtDesigner import QDesignerFormEditorInterface
class PluginBase:
Factory = None
def __init__(self, parent=None):
super().__init__(parent)
self.initialized = False
self.factory = None
self.pattern = re.compile(r'(?<!^)(?=[A-Z])')
def initialize(self, core):
def initialize(self, editor: QDesignerFormEditorInterface):
if self.initialized:
return
self.initialized = True
if not self.Factory:
return
manager = editor.extensionManager()
self.factory = self.Factory(manager)
manager.registerExtensions(self.factory, self.factory.IID)
def isInitialized(self):
return self.initialized
......
# coding: utf-8
from typing import Type
from PyQt5.QtWidgets import QAction, QWidget
from PyQt5.QtDesigner import QPyDesignerTaskMenuExtension, QExtensionFactory, QDesignerFormWindowInterface, QPyDesignerCustomWidgetPlugin
from qfluentwidgets import MessageBox, LineEdit
class EditTextDialog(MessageBox):
def __init__(self, widget: QWidget, parent=None):
super().__init__('Edit text', '', parent)
self.contentLabel.hide()
self.lineEdit = LineEdit(self.widget)
self.propertyName = 'text_' if widget.property('text') is None else 'text'
self.lineEdit.setText(widget.property(self.propertyName))
self.lineEdit.selectAll()
self.lineEdit.setFocus()
self.lineEdit.setClearButtonEnabled(True)
self.lineEdit.setPlaceholderText('Enter the text of button')
self.textLayout.addWidget(self.lineEdit)
self.widget.setFixedSize(
max(self.contentLabel.width(), self.titleLabel.width()) + 48,
self.contentLabel.y() + self.lineEdit.height() + 105
)
class TaskMenuExtensionBase(QPyDesignerTaskMenuExtension):
""" Task menu extension base class """
def __init__(self, widget, parent):
super().__init__(parent)
self.widget = widget
self.editTextAction = QAction('Edit text', None)
self.editTextAction.triggered.connect(self.onEditText)
def taskActions(self):
return [self.editTextAction]
def preferredEditAction(self) -> QAction:
return self.editTextAction
def onEditText(self):
w = EditTextDialog(self.widget, self.widget.window())
window = QDesignerFormWindowInterface.findFormWindow(self.widget)
if w.exec():
window.cursor().setProperty(w.propertyName, w.lineEdit.text())
class EditTextTaskMenuExtension(TaskMenuExtensionBase):
""" Edit text task menu extension """
def taskActions(self):
return [self.editTextAction]
class EditTextIconTaskMenuExtension(TaskMenuExtensionBase):
""" Edit text and icon task menu extension """
def taskActions(self):
return [self.editTextAction, self.editIconAction]
def preferredEditAction(self):
return self.editTextAction
class TaskMenuFactoryBase(QExtensionFactory):
""" Task menu factory base class """
widgets = []
Extention = QPyDesignerTaskMenuExtension
IID = 'org.qt-project.Qt.Designer.TaskMenu'
def createExtension(self, object, iid, parent):
if iid != TaskMenuFactoryBase.IID:
return None
if object.__class__.__name__ not in self.widgets:
return None
return self.Extention(object, parent)
@classmethod
def register(cls, Plugin: Type[QPyDesignerCustomWidgetPlugin]):
if Plugin.__name__ not in cls.widgets:
cls.widgets.append(Plugin().name())
Plugin.Factory = cls
return Plugin
class EditTextTaskMenuFactory(TaskMenuFactoryBase):
""" Edit text task menu factory """
Extention = EditTextTaskMenuExtension
......@@ -115,6 +115,17 @@
<translation></translation>
</message>
</context>
<context>
<name>SwitchButton</name>
<message>
<source>Off</source>
<translation></translation>
</message>
<message>
<source>On</source>
<translation></translation>
</message>
</context>
<context>
<name>CustomColorSettingCard</name>
<message>
......
......@@ -115,6 +115,17 @@
<translation></translation>
</message>
</context>
<context>
<name>SwitchButton</name>
<message>
<source>Off</source>
<translation></translation>
</message>
<message>
<source>On</source>
<translation></translation>
</message>
</context>
<context>
<name>CustomColorSettingCard</name>
<message>
......
......@@ -115,6 +115,17 @@
<translation></translation>
</message>
</context>
<context>
<name>SwitchButton</name>
<message>
<source>Off</source>
<translation></translation>
</message>
<message>
<source>On</source>
<translation></translation>
</message>
</context>
<context>
<name>CustomColorSettingCard</name>
<message>
......
此差异已折叠。
......@@ -3,7 +3,7 @@
<file>images/color_dialog/Clear_black.svg</file>
<file>images/color_dialog/Clear_white.svg</file>
<file>images/color_dialog/HuePanel.png</file>
<file>images/icons/Cancel_black.svg</file>
<file>images/icons/Cancel_white.svg</file>
<file>images/icons/ChevronRight_black.svg</file>
......@@ -330,5 +330,6 @@
<file>images/controls/TreeView.png</file>
<file>images/controls/VariableSizedWrapGrid.png</file>
<file>images/controls/Viewbox.png</file>
<file>images/controls/TitleBar.png</file>
</qresource>
</RCC>
\ No newline at end of file
# coding:utf-8
from enum import Enum
from typing import Union
from PyQt5 import QtGui
from PyQt5.QtXml import QDomDocument
from PyQt5.QtCore import QRectF, Qt, QFile, QObject
......@@ -302,6 +301,17 @@ class Icon(QIcon):
self.fluentIcon = fluentIcon
def toQIcon(icon: Union[QIcon, FluentIconBase, str]) -> QIcon:
""" convet `icon` to `QIcon` """
if isinstance(icon, str):
return QIcon(icon)
if isinstance(icon, FluentIconBase):
return icon.icon()
return icon
class Action(QAction):
""" Fluent action """
......
......@@ -5,9 +5,9 @@ from PyQt5.QtCore import QTranslator, QLocale
class FluentTranslator(QTranslator):
""" Translator of fluent widgets """
def __init__(self, locale: QLocale, parent=None):
def __init__(self, locale: QLocale = None, parent=None):
super().__init__(parent=parent)
self.load(locale)
self.load(locale or QLocale())
def load(self, locale: QLocale):
""" load translation file """
......
......@@ -6,7 +6,7 @@ from PyQt5.QtGui import QDesktopServices, QIcon, QPainter
from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QRadioButton, QToolButton, QApplication, QWidget, QSizePolicy
from ...common.animation import TranslateYAnimation
from ...common.icon import FluentIconBase, drawIcon, isDarkTheme, Theme
from ...common.icon import FluentIconBase, drawIcon, isDarkTheme, Theme, toQIcon
from ...common.icon import FluentIcon as FIF
from ...common.style_sheet import FluentStyleSheet
from ...common.overload import singledispatchmethod
......@@ -38,16 +38,18 @@ class PushButton(QPushButton):
def setIcon(self, icon: Union[QIcon, str, FluentIconBase]):
self.setProperty('hasIcon', icon is not None)
self.setStyle(QApplication.style())
self._icon = icon
self._icon = icon or QIcon()
self.update()
def icon(self):
if isinstance(self._icon, str):
return QIcon(self._icon)
if isinstance(self._icon, FluentIconBase):
return self._icon.icon()
return toQIcon(self._icon)
def setProperty(self, name: str, value) -> bool:
if name != 'icon':
return super().setProperty(name, value)
return self._icon
self.setIcon(value)
return True
def mousePressEvent(self, e):
self.isPressed = True
......@@ -71,7 +73,7 @@ class PushButton(QPushButton):
def paintEvent(self, e):
super().paintEvent(e)
if self._icon is None:
if self.icon().isNull():
return
painter = QPainter(self)
......@@ -141,8 +143,8 @@ class HyperlinkButton(QPushButton):
def getUrl(self):
return self._url
def setUrl(self, url: str):
self._url.setUrl(url)
def setUrl(self, url: Union[str, QUrl]):
self._url = QUrl(url)
url = pyqtProperty(QUrl, getUrl, setUrl)
......@@ -197,12 +199,14 @@ class ToolButton(QToolButton):
self.update()
def icon(self):
if isinstance(self._icon, str):
return QIcon(self._icon)
if isinstance(self._icon, FluentIconBase):
return self._icon.icon()
return toQIcon(self._icon)
def setProperty(self, name: str, value) -> bool:
if name != 'icon':
return super().setProperty(name, value)
return self._icon
self.setIcon(value)
return True
def mousePressEvent(self, e):
self.isPressed = True
......@@ -420,6 +424,7 @@ class SplitPushButton(SplitWidgetBase):
def setText(self, text: str):
self.button.setText(text)
self.adjustSize()
def icon(self):
return self.button.icon()
......@@ -427,6 +432,9 @@ class SplitPushButton(SplitWidgetBase):
def setIcon(self, icon: Union[QIcon, FluentIconBase, str]):
self.button.setIcon(icon)
text_ = pyqtProperty(str, text, setText)
icon_ = pyqtProperty(QIcon, icon, setIcon)
class SplitToolButton(SplitWidgetBase):
""" Split tool button """
......@@ -456,14 +464,10 @@ class SplitToolButton(SplitWidgetBase):
self.__init__(parent)
self.setIcon(icon)
def text(self):
return self.button.text()
def setText(self, text: str):
self.button.setText(text)
def icon(self):
return self.button.icon()
def setIcon(self, icon: Union[QIcon, FluentIconBase, str]):
self.button.setIcon(icon)
\ No newline at end of file
self.button.setIcon(icon)
icon_ = pyqtProperty(QIcon, icon, setIcon)
# coding:utf-8
from typing import Union
from PyQt5.QtCore import pyqtProperty
from PyQt5.QtGui import QIcon, QPainter
from PyQt5.QtWidgets import QWidget
from ...common.icon import FluentIconBase, drawIcon
from ...common.icon import FluentIconBase, drawIcon, toQIcon
from ...common.overload import singledispatchmethod
......@@ -31,11 +32,16 @@ class IconWidget(QWidget):
self.__init__(parent)
self.setIcon(icon)
def getIcon(self):
return toQIcon(self._icon)
def setIcon(self, icon: Union[str, QIcon, FluentIconBase]):
self.icon = icon
self._icon = icon
self.update()
def paintEvent(self, e):
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
drawIcon(self.icon, painter, self.rect())
drawIcon(self._icon, painter, self.rect())
icon = pyqtProperty(QIcon, getIcon, setIcon)
\ No newline at end of file
......@@ -17,31 +17,35 @@ class ProgressBar(QProgressBar):
self._val = 0
self.setFixedHeight(4)
self.useAni = useAni
self._useAni = useAni
self.lightBackgroundColor = QColor(0, 0, 0, 155)
self.darkBackgroundColor = QColor(255, 255, 255, 155)
self.ani = QPropertyAnimation(self, b'val', self)
self._isPaused = False
self._isError = False
self.valueChanged.connect(self._onValueChanged)
self.setValue(0)
@pyqtProperty(float)
def val(self):
def getVal(self):
return self._val
@val.setter
def val(self, v):
def setVal(self, v: float):
self._val = v
self.update()
def setValue(self, value: int):
def isUseAni(self):
return self._useAni
def setUseAni(self, isUSe: bool):
self._useAni = isUSe
def _onValueChanged(self, value):
if not self.useAni:
self._val = value
return super().setValue(value)
return
self.ani.stop()
self.ani.setStartValue(self.value())
self.ani.setEndValue(value)
self.ani.setDuration(150)
self.ani.start()
......@@ -127,6 +131,9 @@ class ProgressBar(QProgressBar):
r = self.height() / 2
painter.drawRoundedRect(0, 0, w, self.height(), r, r)
useAni = pyqtProperty(bool, isUseAni, setUseAni)
val = pyqtProperty(float, getVal, setVal)
class IndeterminateProgressBar(QProgressBar):
""" Indeterminate progress bar """
......
......@@ -145,12 +145,14 @@ class SwitchButton(QWidget):
the position of indicator
"""
super().__init__(parent=parent)
self.text = 'Off'
self._text = self.tr('Off')
self._offText = self.tr('Off')
self._onText = self.tr('On')
self.__spacing = 12
self.indicatorPos = indicatorPos
self.hBox = QHBoxLayout(self)
self.indicator = Indicator(self)
self.label = QLabel(self.text, self)
self.label = QLabel(self._text, self)
self.__initWidget()
@__init__.register
......@@ -168,6 +170,7 @@ class SwitchButton(QWidget):
the position of indicator
"""
self.__init__(parent, indicatorPos)
self._offText = text
self.setText(text)
def __initWidget(self):
......@@ -192,6 +195,7 @@ class SwitchButton(QWidget):
FluentStyleSheet.SWITCH_BUTTON.apply(self)
# connect signal to slot
self.indicator.toggled.connect(self._updateText)
self.indicator.toggled.connect(self.checkedChanged)
def eventFilter(self, obj, e: QEvent):
......@@ -217,15 +221,22 @@ class SwitchButton(QWidget):
def setChecked(self, isChecked):
""" set checked state """
self.adjustSize()
self._updateText()
self.indicator.setChecked(isChecked)
def toggleChecked(self):
""" toggle checked state """
self.indicator.setChecked(not self.indicator.isChecked())
def _updateText(self):
self.setText(self.onText if self.isChecked() else self.offText)
self.adjustSize()
def getText(self):
return self._text
def setText(self, text):
self.text = text
self._text = text
self.label.setText(text)
self.adjustSize()
......@@ -237,4 +248,22 @@ class SwitchButton(QWidget):
self.hBox.setSpacing(spacing)
self.update()
def getOnText(self):
return self._onText
def setOnText(self, text):
self._onText = text
self._updateText()
def getOffText(self):
return self._offText
def setOffText(self, text):
self._offText = text
self._updateText()
spacing = pyqtProperty(int, getSpacing, setSpacing)
checked = pyqtProperty(bool, isChecked, setChecked)
text = pyqtProperty(str, getText, setText)
onText = pyqtProperty(str, getOnText, setOnText)
offText = pyqtProperty(str, getOffText, setOffText)
\ No newline at end of file
......@@ -2,6 +2,9 @@
from distutils.sysconfig import get_python_lib
import os
import sys
import shutil
import warnings
from pathlib import Path
import PyQt5
......@@ -12,16 +15,33 @@ def get_designer_path():
""" get the path of qt designer """
site_packages = get_python_lib()
ext = '.exe' if os.name == 'nt' else ''
bins = [
f"{QLibraryInfo.location(QLibraryInfo.BinariesPath)}/designer{ext}",
f"{site_packages}/pyqt5_tools/designer{ext}",
f"{site_packages}/qt5_applications/Qt/bin/designer{ext}",
]
for f in bins:
if os.path.exists(f):
return f
raise Exception("Can't find avalibale QtDesigner")
path = Path(f"{site_packages}/qt5_applications/Qt/bin/designer{ext}")
if not path.exists():
raise Exception(
"Can't find available QtDesigner for current environment. You can try `pip install pyqt5-tools` to solve this problem.")
# check pyqt5 dll
if sys.platform == "win32":
dll_name = "pyqt5.dll"
elif sys.platform == "darwin":
dll_name = "libpyqt5.dylib"
else:
dll_name = "libpyqt5.so"
dll_path = Path(f"{site_packages}/qt5_applications/Qt/plugins/designer/{dll_name}")
if dll_path.exists():
return str(path)
plugin_dll_path = Path(f"{site_packages}/pyqt5_plugins/Qt/plugins/designer/{dll_name}")
if not plugin_dll_path.exists():
warnings.warn(f"Can't find avaliable {dll_name}, which may cause PyQt-Fluent-Widgets not being visible in QtDesigner.")
return str(path)
# copy pyqt5 dll
dll_path.parent.mkdir(exist_ok=True, parents=True)
shutil.copy(plugin_dll_path, dll_path)
print(f'Copy {plugin_dll_path} to {dll_path}.')
return str(path)
tools_dir = Path(__file__).absolute().parent
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册