Форум программистов, компьютерный форум, киберфорум
iamvic
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
Путевые заметки в процессе познания Python и PyQt/PySide.
Помни - только тег CODE не портит код добавлением пробела в начало пустой строки.

К вопросу о PyQt и самописных виджетах в Qt Designer.

Запись от iamvic размещена 21.12.2023 в 14:27
Показов 1306 Комментарии 0

Иногда, работая над каким-нибудь проектом, приходится сталкиваться с ситуацией, когда в различных окнах (диалогах) часть информации, включая элементы управления, полностью дублируется. Эту общую часть вполне можно оформить в виде отдельного класса на базе QWidget и применять его при создании соответствующих окон (диалогов). Например, так, как это сделано в демонстрационной программе, представленной в теме К вопросу о наследовании QWidget и таблицах стилей.

А как быть, если PyQt-приложение построено на технологии Qt Designer? Это программисты C/C++ могут добавлять в Qt Designer собственные виджеты, а для Python такой возможности не предусмотрено. Впрочем, в Интернете полно подсказок типа "можно в коде добавлять такие виджеты в пустые раскладки", а вот полновесных реализаций этой затеи я так и не нашёл . Сделал свою, пусть будет.

В результате немножко изменилась постановка задачи, поскольку Qt Designer - это всё-таки инструмент системы разделения труда. И участники процесса хоть и должны работать по общему плану с соблюдением всех ритуалов и договорённостей, но в какой-то степени независимо друг от друга. Поэтому программист будет кодить, стилист - макияжить, а дизайнер - дизайнить Ну и на переводчика планы тоже будут построены.



Фвйл style_probe.py:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/python3
# -*- coding: utf-8 -*-
'''Style Probe v0.05
from iamvic on cyberforum.ru
'''
 
import glob, sys
from PyQt5 import uic
from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5.QtGui import QKeySequence, QPainter
from PyQt5.QtWidgets import (
    QApplication, QCheckBox, QDialog, QHBoxLayout, QMainWindow,
    QShortcut, QStyle, QStyleFactory, QStyleOption, QWidget)
 
class Control_widget(QWidget):
    def __init__(self, parent=None):
        super(Control_widget, self).__init__(parent)
        uic.loadUi('style_check.ui', self)
 
    def paintEvent(self, e):
        opt = QStyleOption()
        opt.initFrom(self)
        p = QPainter(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, p, self)
 
class Dialog_window(QDialog):
    def __init__(self, parent=None):
        super(Dialog_window, self).__init__(parent)
        self.outsourcer = self.sender()
        QShortcut(QKeySequence('Ctrl+0'), self, self.close)
        uic.loadUi('style_dialog.ui', self)
        w_layout = self.findChild(QHBoxLayout, 'controllayout')
        ctrl_widget = Control_widget()
        w_layout.addWidget(ctrl_widget)
 
    def closeEvent(self, e):
        w_check = self.findChild(QCheckBox, 'controlcheck')
        if (self.sender() == self.outsourcer and
            w_check is not None and
            not w_check.checkState() == Qt.Unchecked):
 
            e.ignore()
        else:
            e.accept()
 
    @pyqtSlot(name = 'on_closebutton_pressed')
    def to_close_dialog(self):
        self.close()
 
class Main_window(QMainWindow):
    def __init__(self, parent=None):
        super(Main_window, self).__init__(parent)
        self.outsourcer = self.sender()
        QShortcut(QKeySequence('Ctrl+0'), self, self.close)
        uic.loadUi('style_main.ui', self)
        w_layout = self.findChild(QHBoxLayout, 'controllayout')
        ctrl_widget = Control_widget()
        w_layout.addWidget(ctrl_widget)
 
    def closeEvent(self, e):
        w_check = self.findChild(QCheckBox, 'controlcheck')
        if (self.sender() == self.outsourcer and
            w_check is not None and
            not w_check.checkState() == Qt.Unchecked):
 
            e.ignore()
        else:
            e.accept()
 
    @pyqtSlot(name = 'on_closebutton_pressed')
    @pyqtSlot(name = 'on_closeaction_triggered')
    def to_close_application(self):
        self.close()
 
    @pyqtSlot(name = 'on_dialog1button_pressed')
    @pyqtSlot(name = 'on_dialog1action_triggered')
    @pyqtSlot(name = 'on_dialog2button_pressed')
    @pyqtSlot(name = 'on_dialog2action_triggered')
    def to_open_dialog(self):
        i = self.sender().objectName()[6]
 
        dialog = Dialog_window(self)
        dialog.setObjectName('dialog{!s}control'.format(i))
        if i == '2':
            dialog.setWindowFlags(
                Qt.Window |
                Qt.FramelessWindowHint |
                Qt.CustomizeWindowHint
                )
        dialog.exec_()
        dialog.deleteLater()
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
    if 'Windows' in QStyleFactory.keys():
        app.setStyle(QStyleFactory.create('Windows'))
 
    style_set = ''
    for n in [str(n) for n in glob.glob('*.qss')]:
        with open(n, 'r') as f:
            style_set += '{!s}\n'.format(f.read())
    app.setStyleSheet(style_set)
 
    mwin = Main_window()
    mwin.move(200, 200)
    mwin.resize(400, 300)
    mwin.show()
 
    sys.exit(app.exec_())


Фвйл style_main.ui:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>268</width>
    <height>211</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Волевое решение</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QHBoxLayout" name="horizontalLayout">
    <item>
     <spacer name="horizontalSpacer">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>1</width>
        <height>20</height>
       </size>
      </property>
     </spacer>
    </item>
    <item>
     <layout class="QVBoxLayout" name="verticalLayout">
      <item>
       <spacer name="verticalSpacer">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>40</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <layout class="QHBoxLayout" name="controllayout"/>
      </item>
      <item>
       <widget class="QPushButton" name="dialog1button">
        <property name="text">
         <string>Открыть диалог с заголовком</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="dialog2button">
        <property name="text">
         <string>Открыть диалог без заголовка</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="closebutton">
        <property name="text">
         <string>Завершить работу</string>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="verticalSpacer_2">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>40</height>
         </size>
        </property>
       </spacer>
      </item>
     </layout>
    </item>
    <item>
     <spacer name="horizontalSpacer_2">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>0</width>
        <height>20</height>
       </size>
      </property>
     </spacer>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>268</width>
     <height>32</height>
    </rect>
   </property>
   <widget class="QMenu" name="menu">
    <property name="title">
     <string>Действия</string>
    </property>
    <addaction name="dialog1action"/>
    <addaction name="dialog2action"/>
    <addaction name="closeaction"/>
   </widget>
   <addaction name="menu"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <action name="dialog1action">
   <property name="text">
    <string>Открыть диалог с заголовком</string>
   </property>
   <property name="shortcut">
    <string notr="true">Ctrl+1</string>
   </property>
  </action>
  <action name="dialog2action">
   <property name="text">
    <string>Открыть диалог без заголовка</string>
   </property>
   <property name="shortcut">
    <string notr="true">Ctrl+2</string>
   </property>
  </action>
  <action name="closeaction">
   <property name="text">
    <string>Завершить работу</string>
   </property>
   <property name="shortcut">
    <string notr="true">Ctrl+Q</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>


Фвйл style_dialog.ui:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>179</width>
    <height>70</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Волевое решение</string>
  </property>
  <layout class="QHBoxLayout" name="horizontalLayout_2">
   <item>
    <spacer name="horizontalSpacer">
     <property name="orientation">
      <enum>Qt::Horizontal</enum>
     </property>
     <property name="sizeHint" stdset="0">
      <size>
       <width>0</width>
       <height>20</height>
      </size>
     </property>
    </spacer>
   </item>
   <item>
    <layout class="QVBoxLayout" name="verticalLayout">
     <item>
      <spacer name="verticalSpacer">
       <property name="orientation">
        <enum>Qt::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>20</width>
         <height>40</height>
        </size>
       </property>
      </spacer>
     </item>
     <item>
      <layout class="QHBoxLayout" name="controllayout"/>
     </item>
     <item>
      <widget class="QPushButton" name="closebutton">
       <property name="text">
        <string>Завершить работу</string>
       </property>
      </widget>
     </item>
     <item>
      <spacer name="verticalSpacer_2">
       <property name="orientation">
        <enum>Qt::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>20</width>
         <height>40</height>
        </size>
       </property>
      </spacer>
     </item>
    </layout>
   </item>
   <item>
    <spacer name="horizontalSpacer_2">
     <property name="orientation">
      <enum>Qt::Horizontal</enum>
     </property>
     <property name="sizeHint" stdset="0">
      <size>
       <width>0</width>
       <height>20</height>
      </size>
     </property>
    </spacer>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>


Фвйл style_check.ui:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>controlwidget</class>
 <widget class="QWidget" name="controlwidget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>286</width>
    <height>62</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="controllayout">
   <item>
    <widget class="QLabel" name="controllabel">
     <property name="text">
      <string>Про sender() и parent()</string>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QCheckBox" name="controlcheck">
     <property name="text">
      <string>Отключить внешнее управление</string>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>


Фвйл style_probe.qssi:
CSS
1
2
3
4
5
QWidget#centralwidget {background-color: YellowGreen;}
Dialog_window#dialog1control {background-color: PaleGoldenrod;}
Dialog_window#dialog2control {background-color: PaleGoldenrod;}
QLabel#controllabel {font: bold;}
Control_widget#controlwidget {background-color: Red;}


Поскольку у программиста не нашлось слов, требующих перевода , то исходник для переводчика можно делать только из файлов дизайна
Bash
1
pylupdate5 style_main.ui style_dialog.ui style_check.ui -ts style_probe.ts


Фвйл style_probe.ts:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="2.0">
<context encoding="UTF-8">
    <name>Dialog</name>
    <message encoding="UTF-8">
        <location filename="style_dialog.ui" line="51"/>
        <source>Завершить работу</source>
        <translation type="unfinished"></translation>
    </message>
    <message encoding="UTF-8">
        <location filename="style_dialog.ui" line="14"/>
        <source>Волевое решение</source>
        <translation type="unfinished"></translation>
    </message>
</context>
<context encoding="UTF-8">
    <name>MainWindow</name>
    <message encoding="UTF-8">
        <location filename="style_main.ui" line="122"/>
        <source>Открыть диалог с заголовком</source>
        <translation type="unfinished"></translation>
    </message>
    <message encoding="UTF-8">
        <location filename="style_main.ui" line="130"/>
        <source>Открыть диалог без заголовка</source>
        <translation type="unfinished"></translation>
    </message>
    <message encoding="UTF-8">
        <location filename="style_main.ui" line="138"/>
        <source>Завершить работу</source>
        <translation type="unfinished"></translation>
    </message>
    <message encoding="UTF-8">
        <location filename="style_main.ui" line="111"/>
        <source>Действия</source>
        <translation type="unfinished"></translation>
    </message>
    <message encoding="UTF-8">
        <location filename="style_main.ui" line="14"/>
        <source>Волевое решение</source>
        <translation type="unfinished"></translation>
    </message>
</context>
<context>
    <name>controlwidget</name>
    <message>
        <location filename="style_check.ui" line="14"/>
        <source>Form</source>
        <translation type="unfinished"></translation>
    </message>
    <message encoding="UTF-8">
        <location filename="style_check.ui" line="20"/>
        <source>Про sender() и parent()</source>
        <translation type="unfinished"></translation>
    </message>
    <message encoding="UTF-8">
        <location filename="style_check.ui" line="27"/>
        <source>Отключить внешнее управление</source>
        <translation type="unfinished"></translation>
    </message>
</context>
</TS>

Пока переводчики работают, программисту можно дать задание "обеспечить загрузку файлов перевода типа *.qm в трансляторы и их установку"...
Размещено в Памятка
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
[EasyBuilder Pro] Памятка по разработке для панелей Weintek
ФедосеевПавел 26.08.2026
Памятка по разработке для панелей Weintek ВВЕДЕНИЕ Ранее, при реализации проектов основное внимание уделял разработке управляющей программы для контроллера, а панели оператора доставалось время. . .
Модель по догадкам
anaschu 25.08.2026
Прошло две недели. Я уже рассказывал, как разговаривал с сотрудниками у сортировки и как понял, что главная ветка — не про приёмку, а про отбор. Но тогда я думал, что понял механику. На этой неделе я. . .
Запись в регистр сведений независимо от заполненности табличной части
Maks 25.08.2026
Реализация из решения ниже выполнена на нетиповом документе с несколькими табличными частями, разработанного в КА2. Задача: Обеспечить запись документа в регистр сведений независимо от. . .
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru