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

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

Запись от iamvic размещена 21.12.2023 в 14:27
Показов 1284 Комментарии 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
Комментарии
 
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru