1. Огляд
Створення власних віджетів дозволяє використовувати редактор Qt Designer для розміщення власного віджета, замість того, щоб робити це вручну у файлі обробника.
Корисні користувацькі віджети були б чудовим способом зробити свій внесок у LinuxCNC.
1.1. Віджети
Віджет — це загальна назва для об’єктів інтерфейсу користувача, таких як кнопки та мітки в PyQt.
Також існують спеціальні віджети, створені для LinuxCNC, які спрощують інтеграцію.
Всі ці віджети можна розмістити за допомогою редактора Qt Designer, що дозволяє побачити результат перед фактичним завантаженням панелі в LinuxCNC.
1.2. Дизайнер Qt
Qt Designer — це WYSIWYG (що бачиш, те й отримуєш) редактор для розміщення віджетів PyQt.
Спочатку його метою було створення графічних віджетів для програм.
Ми використовуємо його для створення екранів та панелей для LinuxCNC.
У Qt Designer, у лівій частині редактора, ви знайдете три категорії віджетів LinuxCNC:
-
Віджети лише для HAL.
-
Віджети контролера LinuxCNC.
-
діалогові віджети.
Щоб Qt Designer міг додавати власні віджети до свого редактора, потрібно додати плагін до потрібної папки.
1.3. Процес ініціалізації
QtVCP виконує додаткове налаштування для віджетів, підкласованих з _HALWidgetBase, тобто віджетів, "підібраних HAL".
Це включає:
-
Введення важливих змінних,
-
Виклик додаткової функції налаштування
-
Виклик функції очищення під час завершення роботи.
Ці функції не викликаються, коли редактор Qt Designer відображає віджети.
Коли QtVCP створює екран з файлу .ui:
-
Він шукає всі HAL-подібні віджети.
-
Він знаходить віджет
ScreenOptionsдля збору інформації, необхідної для вставки в інші віджети -
Він створює екземпляр кожного віджета, і якщо це HAL-подібний віджет, викликає функцію
hal_init().
Функціяhal_init()визначена в базовому класі та:-
Додає змінні, такі як файл налаштувань, до кожного HAL-подібного віджета.
-
Викличте
+_hal_init()+для віджета.
+_hal_init()+дозволяє розробнику віджета виконувати налаштування, що потребують доступу до додаткових змінних.
-
Ось опис додаткових змінних, введених у віджети, "підібрані до HAL":
-
self.HAL_GCOMP -
Екземпляр компонента HAL
-
self.HAL_NAME -
Назва цього віджета у вигляді рядка
-
self.QT_OBJECT_ -
Цей екземпляр об’єкта віджета
-
self.QTVCP_INSTANCE_ -
Найвищий рівень батьківського елемента екрана
-
self.PATHS_ -
Екземпляр бібліотеки шляхів QtVCP
-
self.PREFS_ -
Екземпляр додаткового файлу налаштувань
-
self.SETTINGS_ -
Екземпляр об’єкта
Qsettings
1.4. процес очищення
Коли QtVCP закривається, він викликає функцію +_hal_cleanup()+ на всіх HAL-подібних віджетах.
Базовий клас створює порожню функцію +_hal_cleanup()+, яку можна перевизначити в підкласі користувацького віджета.
Це можна використовувати для таких дій, як налаштування записів тощо.
Ця функція не викликається, коли редактор Qt Designer відображає віджети.
2. Користувацькі віджети HAL
Найпростішим прикладом є віджети HAL.
Файл qtvcp/widgets/simple_widgets.py містить багато віджетів лише HAL.
Давайте розглянемо фрагмент simple_widgets.py:
Тут ми імпортуємо бібліотеки, необхідні нашому класу віджетів.
#!/usr/bin/env python3 ############################### # Імпорт ############################### from PyQt5 import QtWidgets # <1> from qtvcp.widgets.widget_baseclass \ import _HalWidgetBase, _HalSensitiveBase # <2> import hal # <3>
У цьому випадку нам потрібен доступ до:
-
Бібліотека QtWidgets від PyQt,
-
Бібліотека HAL від LinuxCNC та
-
Віджет QtVCP
baseclass's_HalSensitiveBaseдля автоматичної настройки контактів HAL та для відключення/включення віджета (також відомого як чутливість вводу).
У бібліотеці також доступні функції_HalToggleBaseта_HalScaleBase.
Ось користувацький віджет, заснований на віджеті QGridLayout від PyQt.
QGridLayout дозволяє:
-
Розташуйте об’єкти у вигляді сітки.
-
Увімкнути/вимкнути всі віджети всередині на основі стану виводу HAL.
###################### # WIDGET ###################### class Lcnc_GridLayout(QtWidgets.QWidget, _HalSensitiveBase): # <1> def __init__(self, parent = None): # <2> super(GridLayout, self).__init__(parent) # <3>
Рядок за рядком:
-
Це визначає ім’я класу та бібліотеки, від яких він успадковує.
Цей клас, названийLcnc_GridLayout, успадковує функціїQWidgetта+_HalSensitiveBase+.
+_HalSensitiveBase+є «підкласом»+_HalWidgetBase+, базовим класом більшості віджетів QtVCP, що означає, що він має всі функції+_HalWidgetBase+плюс функції+_HalSensitiveBase+.
Він додає функцію, яка дозволяє вмикати або вимикати віджет на основі вхідного BIT-контакту HAL. -
Це функція, яка викликається, коли віджет вперше створюється (тобто створюється його екземпляр) – це досить стандартно.
-
Ця функція ініціалізує
Superкласи нашого віджета.
Superозначає просто успадковані базові класи, тобтоQWidgetі_HalSensitiveBase.
Досить стандартно, за винятком того, що змінюється назва віджета.
3. Віджети користувацького контролера з використанням STATUS
Віджети, що взаємодіють з контролером LinuxCNC, лише трохи складніші та потребують деяких додаткових бібліотек.
У цьому скороченому прикладі ми додамо властивості, які можна змінити в Qt Designer.
Цей віджет світлодіодного індикатора реагуватиме на вибрані стани контролера LinuxCNC.
#!/usr/bin/env python3 ############################### # Імпорт ############################### from PyQt5.QtCore import pyqtProperty from qtvcp.widgets.led_widget import LED from qtvcp.core import Status ########################################### # **** розділ створення екземплярів бібліотек **** # ########################################### STATUS = Status() ########################################## # визначення класу користувацького віджета ########################################## class StateLED(LED): def __init__(self, parent=None): super(StateLED, self).__init__(parent) self.has_hal_pins = False self.setState(False) self.is_estopped = False self.is_on = False self.invert_state = False def _hal_init(self): if self.is_estopped: STATUS.connect('state-estop', lambda w:self._flip_state(True)) STATUS.connect('state-estop-reset', lambda w:self._flip_state(False)) elif self.is_on: STATUS.connect('state-on', lambda w:self._flip_state(True)) STATUS.connect('state-off', lambda w:self._flip_state(False)) def _flip_state(self, data): if self.invert_state: data = not data self.change_state(data) ######################################################################### # Встановлювачі/геттери/скидання властивостей Qt Designer ######################################################################## # інвертований статус def set_invert_state(self, data): self.invert_state = data def get_invert_state(self): return self.invert_state def reset_invert_state(self): self.invert_state = False # стан машини зупинено def set_is_estopped(self, data): self.is_estopped = data def get_is_estopped(self): return self.is_estopped def reset_is_estopped(self): self.is_estopped = False # стан машини увімкнено def set_is_on(self, data): self.is_on = data def get_is_on(self): return self.is_on def reset_is_on(self): self.is_on = False ####################################### # Властивості Qt Designer ####################################### invert_state_status = pyqtProperty(bool, get_invert_state, set_invert_state, reset_invert_state) is_estopped_status = pyqtProperty(bool, get_is_estopped, set_is_estopped, reset_is_estopped) is_on_status = pyqtProperty(bool, get_is_on, set_is_on, reset_is_on)
3.1. У розділі «Імпорт»
Тут ми імпортуємо бібліотеки, необхідні нашому класу віджетів.
#!/usr/bin/env python3 ############################### # Імпорт ############################### from PyQt5.QtCore import pyqtProperty # <1> from qtvcp.widgets.led_widget import LED # <2> from qtvcp.core import Status # <3>
Ми імпортуємо
-
pyqtPropertyщоб ми могли взаємодіяти з редактором Qt Designer, -
LEDоскільки наш власний віджет базується на ньому, -
Statusтому що він надає нам повідомлення про стан від LinuxCNC.
3.2. У розділі «Створення миттєвих бібліотек»
Тут ми створюємо екземпляр бібліотеки Status:
########################################### # **** розділ створення екземплярів бібліотек **** # ########################################### STATUS = Status()
Зазвичай ми створювали екземпляр бібліотеки поза класом віджета, щоб посилання на неї було глобальним, тобто вам не потрібно було використовувати self перед ним.
За домовленістю ми використовуємо всі великі літери в назві для глобальних посилань.
3.3. У розділі «Визначення класу користувацького віджета»
Це суть нашого власного віджета.
class StateLed(LED): # <1> def __init__(self, parent=None): # <2> super(StateLed, self).__init__(parent) # <3> self.has_hal_pins = False # <4> self.setState(False) # <5> self.is_estopped = False self.is_on = False self.invert_state = False
-
Визначає ім’я нашого користувацького віджета та від якого іншого класу він успадковується.
У цьому випадку ми успадковуємоLED- віджет QtVCP, який представляє індикатор стану. -
Типово для більшості віджетів — викликається, коли віджет вперше створюється.
-
Типово для більшості віджетів — викликає код ініціалізації батьківського (супер) віджета.
Потім ми встановлюємо деякі атрибути:
-
Успадковано від
Lcnc_Led- ми встановлюємо його тут, щоб не створювати контакт HAL. -
Успадковано від
Lcnc_led— ми встановлюємо його, щоб переконатися, що світлодіод вимкнено.
Інші атрибути стосуються опцій, які можна вибрати для нашого віджета.
def _hal_init(self): if self.is_estopped: STATUS.connect('state-estop', lambda w:self._flip_state(True)) STATUS.connect('state-estop-reset', lambda w:self._flip_state(False)) elif self.is_on: STATUS.connect('state-on', lambda w:self._flip_state(True)) STATUS.connect('state-off', lambda w:self._flip_state(False))
Ця функція підключає STATUS (бібліотеку повідомлень про стан LinuxCNC) до нашого віджета, щоб світлодіод вмикався або вимикався залежно від вибраного стану контролера.
Ми можемо вибрати два стани: is_estopped або is_on.
Залежно від того, який із них активний, наш віджет підключається до відповідних повідомлень STATUS.
+_hal_init()+ викликається для кожного віджета, який успадковує +_HalWidgetBase+, коли QtVCP вперше будує екран.
Ви можете запитати, чому вона викликається для цього віджета, оскільки ми не мали +_HalWidgetBase+ у нашому визначенні класу (class Lcnc_State_Led(Lcnc_Led):) — вона викликається, оскільки Lcnc_Led успадковує +_HalWidgetBase+.
У цій функції ви маєте доступ до деякої додаткової інформації (хоча ми не використовуємо її в цьому прикладі):
-
self.HAL_GCOMP -
екземпляр компонента HAL
-
self.HAL_NAME -
Назва цього віджета у вигляді рядка
-
self.QT_OBJECT_ -
Екземпляр об’єкта PyQt цього віджета
-
self.QTVCP_INSTANCE_ -
Найвищий рівень батьківського елемента екрана
-
self.PATHS_ -
Екземпляр бібліотеки path у QtVCP
-
self.PREFS_ -
екземпляр додаткового файлу налаштувань
-
self.SETTINGS_ -
об’єкт
Qsettings
Ми могли б використовувати цю інформацію для створення HAL-пінів або пошуку шляхів до зображень тощо.
STATUS.connect('state-estop', lambda w:self._flip_state(True))
Давайте розглянемо цей рядок уважніше:
-
STATUS— дуже поширена тема для створення віджетів.
STATUSвикористовує систему повідомленьGObjectдля надсилання повідомлень віджетам, які реєструються в ньому.
Цей рядок відповідає за процес реєстрації. -
«state-estop» – це повідомлення, яке ми хочемо прослухати та вжити заходів. Існує багато доступних повідомлень.
-
lambda w:self._flip_state(True)— це те, що відбувається, коли повідомлення перехоплюється.
Функція lambda приймає екземпляр віджета (w), який надсилає GObject, а потім викликає функціюself._flip_state(True).
Lambda використовувалася для видалення об’єкта (w) перед викликом функціїself._flip_state.
Вона також дозволяла надсилатиself._flip_state()стан True.
def _flip_state(self, data): if self.invert_state: data = not data self.change_state(data)
Це функція, яка фактично змінює стан світлодіода.
Вона викликається, коли прийнято відповідне повідомлення STATUS.
STATUS.connect('current-feed-rate', self._set_feedrate_text)
Викликана функція виглядає так:
def _set_feedrate_text(self, widget, data):
в якому віджет та будь-які дані повинні бути прийняті функцією.
3.3.1. У розділі «Властивості дизайнера: сеттери/отримання/скидання»
######################################################################### # Встановлювачі/геттери/скидання властивостей Qt Designer ######################################################################## # інвертований статус def set_invert_state(self, data): self.invert_state = data def get_invert_state(self): return self.invert_state def reset_invert_state(self): self.invert_state = False # стан машини зупинено def set_is_estopped(self, data): self.is_estopped = data def get_is_estopped(self): return self.is_estopped def reset_is_estopped(self): self.is_estopped = False # стан машини увімкнено def set_is_on(self, data): self.is_on = data def get_is_on(self): return self.is_on def reset_is_on(self): self.is_on = False
Ось так Qt Designer встановлює атрибути віджета.
Це також можна викликати безпосередньо у віджеті.
3.3.2. У розділі «Властивості конструктора»
####################################### # Властивості Qt Designer ####################################### invert_state_status = pyqtProperty(bool, get_invert_state, set_invert_state, reset_invert_state) is_estopped_status = pyqtProperty(bool, get_is_estopped, set_is_estopped, reset_is_estopped) is_on_status = pyqtProperty(bool, get_is_on, set_is_on, reset_is_on)
Це реєстрація властивостей у Qt Designer.
Назва власності:
-
це текст, що використовується в Qt Designer,
-
не може бути таким самим, як атрибути, які вони представляють.
Ці властивості відображаються в Qt Designer у порядку, в якому вони тут з’являються.
4. Віджети налаштованого контролера з діями
Ось приклад віджета, який встановлює систему відліку користувача.
Це змінюється:
-
стан контролера машини за допомогою бібліотеки
ACTION, -
чи можна натиснути кнопку, використовуючи бібліотеку
STATUS.
import os import hal from PyQt5.QtWidgets import QWidget, QToolButton, QMenu, QAction from PyQt5.QtCore import Qt, QEvent, pyqtProperty, QBasicTimer, pyqtSignal from PyQt5.QtGui import QIcon from qtvcp.widgets.widget_baseclass import _HalWidgetBase from qtvcp.widgets.dialog_widget import EntryDialog from qtvcp.core import Status, Action, Info # Інстанціювання бібліотек із глобальним посиланням # STATUS надає нам повідомлення про стан від LinuxCNC # INFO містить деталі INI # ACTION надає команди LinuxCNC STATUS = Status() INFO = Info() ACTION = Action() class SystemToolButton(QToolButton, _HalWidgetBase): def __init__(self, parent=None): super(SystemToolButton, self).__init__(parent) self._joint = 0 self._last = 0 self._block_signal = False self._auto_label_flag = True SettingMenu = QMenu() for system in('G54', 'G55', 'G56', 'G57', 'G58', 'G59', 'G59.1', 'G59.2', 'G59.3'): Button = QAction(QIcon('exit24.png'), system, self) Button.triggered.connect(self[system.replace('.','_')]) SettingMenu.addAction(Button) self.setMenu(SettingMenu) self.dialog = EntryDialog() def _hal_init(self): if not self.text() == '': self._auto_label_flag = False def homed_on_test(): return (STATUS.machine_is_on() and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED)) STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect('interp-idle', lambda w: self.setEnabled(homed_on_test())) STATUS.connect('interp-run', lambda w: self.setEnabled(False)) STATUS.connect('all-homed', lambda w: self.setEnabled(True)) STATUS.connect('not-all-homed', lambda w, data: self.setEnabled(False)) STATUS.connect('interp-paused', lambda w: self.setEnabled(True)) STATUS.connect('user-system-changed', self._set_user_system_text) def G54(self): ACTION.SET_USER_SYSTEM('54') def G55(self): ACTION.SET_USER_SYSTEM('55') def G56(self): ACTION.SET_USER_SYSTEM('56') def G57(self): ACTION.SET_USER_SYSTEM('57') def G58(self): ACTION.SET_USER_SYSTEM('58') def G59(self): ACTION.SET_USER_SYSTEM('59') def G59_1(self): ACTION.SET_USER_SYSTEM('59.1') def G59_2(self): ACTION.SET_USER_SYSTEM('59.2') def G59_3(self): ACTION.SET_USER_SYSTEM('59.3') def _set_user_system_text(self, w, data): convert = { 1:"G54", 2:"G55", 3:"G56", 4:"G57", 5:"G58", 6:"G59", 7:"G59.1", 8:"G59.2", 9:"G59.3"} if self._auto_label_flag: self.setText(convert[int(data)]) def ChangeState(self, joint): if int(joint) != self._joint: self._block_signal = True self.setChecked(False) self._block_signal = False self.hal_pin.set(False) ############################## # код необхідного класу котла # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
5. Зміни властивостей таблиці стилів на основі подій
Можна змінити стиль віджетів у разі зміни подій.
Ви повинні явно "відполірувати" віджет, щоб PyQt переробив стиль.
Це відносно дорога функція, тому її слід використовувати економно.
У цьому прикладі встановлюється властивість isHomed на основі стану homed у LinuxCNC та, у свою чергу, використовується для зміни властивостей таблиці стилів:
class HomeLabel(QLabel, _HalWidgetBase): def __init__(self, parent=None): super(HomeLabel, self).__init__(parent) self.joint_number = 0 # для читання таблиць стилів self._isHomed = False def _hal_init(self): super(HomeLabel, self)._hal_init() STATUS.connect('homed', lambda w,d: self._home_status_polish(int(d), True)) STATUS.connect('unhomed', lambda w,d: self._home_status_polish(int(d), False)) # оновити властивість ishomed # полірувати віджет, щоб таблиця стилів бачила зміну властивості # деякі таблиці стилів забарвлюють текст на home/unhome def _home_status_polish(self, d, state): if self.joint_number = d: self.setProperty('isHomed', state) self.style().unpolish(self) self.style().polish(self) # Геттер та сеттер Qproperty def getisHomed(self): return self._isHomed def setisHomed(self, data): self._isHomed = data # Qproperty isHomed = QtCore.pyqtProperty(bool, getisHomed, setisHomed)
Ось зразок таблиці стилів для зміни кольору тексту залежно від штату.
У цьому випадку будь-який віджет, заснований на віджеті HomeLabel вище, змінить колір тексту.
Зазвичай ви вибираєте певні віджети за допомогою HomeLabel #specific_widget_name[homed=true]:
HomeLabel[homed=true] {
color: green;
}
HomeLabel[homed=false] {
color: red;
}
6. Використання таблиць стилів для зміни властивостей власного віджета
class Label(QLabel): def __init__(self, parent=None): super(Label, self).__init__(parent) alternateFont0 = self.font # Геттер та сеттер Qproperty def getFont0(self): return self.aleternateFont0 def setFont0(self, value): self.alternateFont0(value) # Qproperty styleFont0 = pyqtProperty(QFont, getFont0, setFont0)
Зразок таблиці стилів, яка встановлює властивість власного віджета.
Label{
qproperty-styleFont0: "Times,12,-1,0,90,0,0,0,0,0";
}
7. Плагіни віджетів
Ми повинні зареєструвати наш користувацький віджет, щоб Qt Designer міг його використовувати.
Ось типові приклади.
Їх потрібно додати до qtvcp/plugins/.
Потім qtvcp/plugins/qtvcp_plugin.py потрібно буде налаштувати для їх імпорту.
7.1. Приклад сітки
#!/usr/bin/env python3 from PyQt5 import QtCore, QtGui from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin from qtvcp.widgets.simple_widgets import Lcnc_GridLayout from qtvcp.widgets.qtvcp_icons import Icon ICON = Icon() #################################### # GridLayout #################################### class LcncGridLayoutPlugin(QPyDesignerCustomWidgetPlugin): def __init__(self, parent = None): QPyDesignerCustomWidgetPlugin.__init__(self) self.initialized = False def initialize(self, formEditor): if self.initialized: return self.initialized = True def isInitialized(self): return self.initialized def createWidget(self, parent): return Lcnc_GridLayout(parent) def name(self): return "Lcnc_GridLayout" def group(self): return "LinuxCNC - HAL" def icon(self): return QtGui.QIcon(QtGui.QPixmap(ICON.get_path('lcnc_gridlayout'))) def toolTip(self): return "HAL enable/disable GridLayout widget" def whatsThis(self): return "" def isContainer(self): return True def domXml(self): return '<widget class="Lcnc_GridLayout" name="lcnc_gridlayout" />\n' def includeFile(self): return "qtvcp.widgets.simple_widgets"
7.2. Приклад кнопки SystemTool
#!/usr/bin/env python3 from PyQt5 import QtCore, QtGui from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin from qtvcp.widgets.system_tool_button import SystemToolButton from qtvcp.widgets.qtvcp_icons import Icon ICON = Icon() #################################### # SystemToolButton #################################### class SystemToolButtonPlugin(QPyDesignerCustomWidgetPlugin): def __init__(self, parent = None): super(SystemToolButtonPlugin, self).__init__(parent) self.initialized = False def initialize(self, formEditor): if self.initialized: return self.initialized = True def isInitialized(self): return self.initialized def createWidget(self, parent): return SystemToolButton(parent) def name(self): return "SystemToolButton" def group(self): return "LinuxCNC - Controller" def icon(self): return QtGui.QIcon(QtGui.QPixmap(ICON.get_path('systemtoolbutton'))) def toolTip(self): return "Button for selecting a User Coordinate System" def whatsThis(self): return "" def isContainer(self): return False def domXml(self): return '<widget class="SystemToolButton" name="systemtoolbutton" />\n' def includeFile(self): return "qtvcp.widgets.system_tool_button"
7.3. Створення плагіна з діалоговим вікном MenuEntry
Можна додати запис до діалогового вікна, яке з’являється, коли ви клацнете правою кнопкою миші на віджеті в макеті.
Це може робити такі речі, як вибір опцій зручнішим способом.
Це плагін, який використовується для кнопок дій.
#!/usr/bin/env python3 import sip from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin, \ QPyDesignerTaskMenuExtension, QExtensionFactory, \ QDesignerFormWindowInterface, QPyDesignerMemberSheetExtension from qtvcp.widgets.action_button import ActionButton from qtvcp.widgets.qtvcp_icons import Icon ICON = Icon() Q_TYPEID = { 'QDesignerContainerExtension': 'org.qt-project.Qt.Designer.Container', 'QDesignerPropertySheetExtension': 'org.qt-project.Qt.Designer.PropertySheet', 'QDesignerTaskMenuExtension': 'org.qt-project.Qt.Designer.TaskMenu', 'QDesignerMemberSheetExtension': 'org.qt-project.Qt.Designer.MemberSheet' } #################################### # ActionBUTTON #################################### class ActionButtonPlugin(QPyDesignerCustomWidgetPlugin): # Метод __init__() використовується лише для налаштування плагіна та визначення його # ініціалізована змінна. def __init__(self, parent=None): super(ActionButtonPlugin, self).__init__(parent) self.initialized = False # Методи initialize() та isInitialized() дозволяють плагіну налаштувати # будь-які необхідні ресурси, гарантуючи, що це може статися лише один раз для кожного # плагіна. def initialize(self, formEditor): if self.initialized: return manager = formEditor.extensionManager() if manager: self.factory = ActionButtonTaskMenuFactory(manager) manager.registerExtensions(self.factory, Q_TYPEID['QDesignerTaskMenuExtension']) self.initialized = True def isInitialized(self): return self.initialized # Цей фабричний метод створює нові екземпляри нашого власного віджета def createWidget(self, parent): return ActionButton(parent) # Цей метод повертає назву класу користувацького віджета def name(self): return "ActionButton" # Повертає назву групи у вікні віджета Qt Designer def group(self): return "LinuxCNC - Controller" # Повертає значок def icon(self): return QtGui.QIcon(QtGui.QPixmap(ICON.get_path('actionbutton'))) # Повертає короткий опис підказки def toolTip(self): return "Action button widget" # Повертає короткий опис користувацького віджета для використання в розділі «Що # "Це?" довідкове повідомлення для віджета. def whatsThis(self): return "" # Повертає значення True, якщо користувацький віджет діє як контейнер для інших віджетів; def isContainer(self): return False # Повертає XML-опис екземпляра користувацького віджета, який описує # значення за замовчуванням для його властивостей. def domXml(self): return '<widget class="ActionButton" name="actionbutton" />\n' # Повертає модуль, що містить клас користувацького віджета. Він може містити # шлях до модуля. def includeFile(self): return "qtvcp.widgets.action_button" class ActionButtonDialog(QtWidgets.QDialog): def __init__(self, widget, parent = None): QtWidgets.QDialog.__init__(self, parent) self.widget = widget self.previewWidget = ActionButton() buttonBox = QtWidgets.QDialogButtonBox() okButton = buttonBox.addButton(buttonBox.Ok) cancelButton = buttonBox.addButton(buttonBox.Cancel) okButton.clicked.connect(self.updateWidget) cancelButton.clicked.connect(self.reject) layout = QtWidgets.QGridLayout() self.c_estop = QtWidgets.QCheckBox("Estop Action") self.c_estop.setChecked(widget.estop ) layout.addWidget(self.c_estop) layout.addWidget(buttonBox, 5, 0, 1, 2) self.setLayout(layout) self.setWindowTitle(self.tr("Налаштування параметрів")) def updateWidget(self): formWindow = QDesignerFormWindowInterface.findFormWindow(self.widget) if formWindow: formWindow.cursor().setProperty("estop_action", QtCore.QVariant(self.c_estop.isChecked())) self.accept() клас ActionButtonMenuEntry(QPyDesignerTaskMenuExtension): def __init__(self, widget, parent): super(QPyDesignerTaskMenuExtension, self).__init__(parent) self.widget = widget self.editStateAction = QtWidgets.QAction( self.tr("Set Options..."), self) self.editStateAction.triggered.connect(self.updateOptions) def preferredEditAction(self): return self.editStateAction def taskActions(self): return [self.editStateAction] def updateOptions(self): dialog = ActionButtonDialog(self.widget) dialog.exec_() class ActionButtonTaskMenuFactory(QExtensionFactory): def __init__(self, parent = None): QExtensionFactory.__init__(self, parent) def createExtension(self, obj, iid, parent): if not isinstance(obj, ActionButton): return None if iid == Q_TYPEID['QDesignerTaskMenuExtension']: return ActionButtonMenuEntry(obj, parent) elif iid == Q_TYPEID['QDesignerMemberSheetExtension']: return ActionButtonMemberSheet(obj, parent) return None