Забавно, что по иным причинам, но именно к первому апреля пришлось приводить к товарному виду пару своих демонстрашек, динамически размещающих и удаляющих индикаторы QProgressBar в центральном виджете главного окна.
В первом случае каждое нажатие кнопки Пуск приводит к реальному запуску в дополнительном потоке нового исполнителя, который симулирует бурную деятельность (проще говоря, спит), периодически информируя пользователя о якобы проделанной работе отправкой сигнала соответствующему индикатору QProgressBar.
Во втором случае никто такими благоглупостями заниматься не собирается. Зачем нам исполнитель, если его деятельность можно вполне успешно имитировать? Ничто не мешает в ответ на получение сигнала valueChanged от индикатора QProgressBar посылать свой сигнал с новым значением хода выполнения с некоторой задержкой по времени, используя специально обученый таймер.
А, впрочем, какая разница? Симулятор или имитатор - результат-то налицо 

Симулятор:
| 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
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
| #!/usr/bin/python3
# -*- coding: utf-8 -*-
"""Dynamic Central Probe v0.001
Добавление и удаление дочерних виджетов
по запросу пользователя (симулятор).
"""
import sys, copy, random
from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR
from PyQt5.QtGui import QCursor
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import QMutex, QMutexLocker
from PyQt5.QtCore import QObject, QTimer, QThread
from PyQt5.QtWidgets import QApplication, QMessageBox
from PyQt5.QtWidgets import QWidget, QPushButton
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QMainWindow, QProgressBar
APP_NAME = "Dynamic Central Probe v0.001"
class Thread_worker(QThread):
thread_mutex = QMutex()
timeout_value = 0
receiver_method = None
maximum_value = 0
transit_signal = pyqtSignal(int)
def set_timeout_value(self, value):
""" для наружного применения
value - значение типа int
задержка отправки сигнала transit_signal в милисекундах
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.timeout_value = copy.copy(value)
if type(self.timeout_value) == int:
rc = True
return rc
def set_maximum_value(self, value):
""" для наружного применения
value - значение типа int
максимальное значение, которое может быть отправлено получателю
с помощью сигнала transit_signal
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.maximum_value = copy.copy(value)
if type(self.maximum_value) == int:
rc = True
return rc
def set_receiver_method(self, value):
""" для наружного применения
value - значение типа builtin_function_or_method
функция или метод - получатель сигнала transit_signal
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.receiver_method = copy.copy(value)
if (str(type(self.receiver_method)) ==
"<class 'builtin_function_or_method'>"):
rc = True
return rc
def get_timeout_value(self):
""" для внутреннего применения
возвращает значение типа int
задержка отправки сигнала transit_signal в милисекундах
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.timeout_value)
return rc
def get_maximum_value(self):
""" для наружного применения
value - значение типа int
максимальное значение, которое может быть отправлено получателю
с помощью сигнала transit_signal
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.maximum_value)
return rc
def get_receiver_method(self):
""" для внутреннего применения
возвращает значение типа builtin_function_or_method
функция или метод - получатель сигнала transit_signal
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.receiver_method)
return rc
def run(self):
""" для внутреннего применения
выполняется в дополнительном потоке после вызова QThread.start()
"""
self.transit_signal.connect(self.get_receiver_method())
i = 0
while (i <= self.get_maximum_value() and
not self.isInterruptionRequested()):
QThread.msleep(self.get_timeout_value())
i += 1
self.transit_signal.emit(i)
class Probe_main_window(QMainWindow):
"""Главное окно приложения
"""
def __init__(self, parent=None):
super(Probe_main_window, self).__init__(parent)
self.init_ui()
def init_ui(self):
def probe_cursor_screen():
"""Определяем экран, на котором находится указатель мыши.
Необходимо для Linux и Windows не портит.
"""
selected_screen = QApplication.screens()[0]
if (int(QT_VERSION_STR.split('.')[1]) >= 10 and
int(PYQT_VERSION_STR.split('.')[1]) >= 10):
selected_screen = QApplication.screenAt(QCursor.pos())
else:
screens = QApplication.screens()
if len(screens) > 0:
for i in range(len(screens)):
if screens[i].geometry().contains(QCursor.pos()):
selected_screen = screens[i]
break
return selected_screen
self.setWindowTitle(APP_NAME)
self.statusBar()
self.central_widget = QWidget(self)
add_button = QPushButton("Пуск", self)
hbox = QHBoxLayout()
hbox.addWidget(add_button)
hbox.addStretch(1)
self.central_widget.vbox = QVBoxLayout()
main_box = QVBoxLayout()
main_box.addLayout(self.central_widget.vbox)
main_box.addStretch(1)
main_box.addLayout(hbox)
self.central_widget.setLayout(main_box)
self.setCentralWidget(self.central_widget)
add_button.clicked.connect(self.to_start_thread)
# Центруем и меняем размеры окна под экран, на котором находится
# указатель мыши (необходимо для Linux и Windows не портит)
selected_screen = probe_cursor_screen()
current_geo = selected_screen.availableGeometry()
self.move(current_geo.x() + current_geo.width() // 4,
current_geo.y() + current_geo.height() // 4)
self.resize(current_geo.width() // 2,
current_geo.height() // 2)
def to_start_thread(self):
pbar = QProgressBar(self)
pbar.setRange(0, random.randint(1, 10)*10)
pbar.valueChanged.connect(self.to_pbar_valuechanged)
pbar.setValue(0)
self.central_widget.vbox.addWidget(pbar)
thread = Thread_worker(self)
if (thread.set_timeout_value(random.choice([100,150,200])) and
thread.set_receiver_method(pbar.setValue) and
thread.set_maximum_value(pbar.maximum())):
thread.finished.connect(thread.deleteLater)
thread.start()
else:
pbar.deleteLater()
thread.deleteLater()
QMessageBox.warning(
self, "О Ш И Б К А !!!",
"Недопустимый тип параметра при вызове\n"
"Thread_worker.set_timeout_value(),\n"
"Thread_worker.set_receiver_method() или\n"
"Thread_worker.set_maximum_value()."
)
def to_pbar_valuechanged(self, value):
if (value >= self.sender().maximum()):
self.central_widget.vbox.removeWidget(self.sender())
self.sender().deleteLater()
def closeEvent(self, e):
# если есть незавершённые дополнительные потоки, то:
# 1. отказываем в закрытии приложения,
# 2. запрашиваем завершение потоков
# 3. выполняем отложенный повтор закрытия приложения
# если нет незавершённых дополнительных потоков, то:
# 1. соглашаемся с закрытием приложения
is_found = False
for t in self.children():
if type(t) == Thread_worker:
is_found = True
t.requestInterruption()
if is_found:
QTimer.singleShot(200, self.close)
e.ignore()
else:
e.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
mwin = Probe_main_window()
mwin.show()
sys.exit(app.exec_()) |
|
Версия имитатора убрана под спойлер для истории в связи обнаружением недоработки
Кликните здесь для просмотра всего текста
Имитатор:
| 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
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
| #!/usr/bin/python3
# -*- coding: utf-8 -*-
"""Dynamic Central Probe v0.002
Добавление и удаление дочерних виджетов
по запросу пользователя (имитатор).
"""
import sys, copy, random
from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR
from PyQt5.QtGui import QCursor
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import QMutex, QMutexLocker
from PyQt5.QtCore import QObject, QThread
from PyQt5.QtWidgets import QApplication, QMessageBox
from PyQt5.QtWidgets import QWidget, QPushButton
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QMainWindow, QProgressBar
APP_NAME = "Dynamic Central Probe v0.002"
class Timer_with_int_transit_value(QThread):
thread_mutex = QMutex()
timeout_value = 0
receiver_method = None
transit_value = 0
transit_signal = pyqtSignal(int)
def set_timeout_value(self, value):
""" для наружного применения
value - значение типа int
задержка отправки сигнала transit_signal в милисекундах
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.timeout_value = copy.copy(value)
if type(self.timeout_value) == int:
rc = True
return rc
def set_transit_value(self, value):
""" для наружного применения
value - значение типа int
значение, отправляемое получателю с помощью сигнала transit_signal
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.transit_value = copy.copy(value)
if type(self.transit_value) == int:
rc = True
return rc
def set_receiver_method(self, value):
""" для наружного применения
value - значение типа builtin_function_or_method
функция или метод - получатель сигнала transit_signal
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.receiver_method = copy.copy(value)
if (str(type(self.receiver_method)) ==
"<class 'builtin_function_or_method'>"):
rc = True
return rc
def get_timeout_value(self):
""" для внутреннего применения
возвращает значение типа int
задержка отправки сигнала transit_signal в милисекундах
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.timeout_value)
return rc
def get_transit_value(self):
""" для внутреннего применения
возвращает значение типа int
значение, отправляемое получателю с помощью сигнала transit_signal
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.transit_value)
return rc
def get_receiver_method(self):
""" для внутреннего применения
возвращает значение типа builtin_function_or_method
функция или метод - получатель сигнала transit_signal
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.receiver_method)
return rc
def run(self):
""" для внутреннего применения
выполняется в дополнительном потоке после вызова QThread.start()
"""
self.transit_signal.connect(self.get_receiver_method())
QThread.msleep(self.get_timeout_value())
self.transit_signal.emit(self.get_transit_value())
def single_shot(client, timeout_value, receiver_method, transit_value):
""" для наружного применения
подобие QTimer.singleShot(timeout_value, receiver_method)
"""
thread = Timer_with_int_transit_value(client)
if (thread.set_timeout_value(timeout_value) and
thread.set_receiver_method(receiver_method) and
thread.set_transit_value(transit_value)):
thread.finished.connect(thread.deleteLater)
thread.start()
return True
else:
thread.deleteLater()
#del thread
return False
class Probe_main_window(QMainWindow):
"""Главное окно приложения
"""
def __init__(self, parent=None):
super(Probe_main_window, self).__init__(parent)
self.init_ui()
def init_ui(self):
def probe_cursor_screen():
"""Определяем экран, на котором находится указатель мыши.
Необходимо для Linux и Windows не портит.
"""
selected_screen = QApplication.screens()[0]
if (int(QT_VERSION_STR.split('.')[1]) >= 10 and
int(PYQT_VERSION_STR.split('.')[1]) >= 10):
selected_screen = QApplication.screenAt(QCursor.pos())
else:
screens = QApplication.screens()
if len(screens) > 0:
for i in range(len(screens)):
if screens[i].geometry().contains(QCursor.pos()):
selected_screen = screens[i]
break
return selected_screen
self.setWindowTitle(APP_NAME)
self.statusBar()
self.central_widget = QWidget(self)
add_button = QPushButton("Пуск", self)
hbox = QHBoxLayout()
hbox.addWidget(add_button)
hbox.addStretch(1)
self.central_widget.vbox = QVBoxLayout()
main_box = QVBoxLayout()
main_box.addLayout(self.central_widget.vbox)
main_box.addStretch(1)
main_box.addLayout(hbox)
self.central_widget.setLayout(main_box)
self.setCentralWidget(self.central_widget)
add_button.clicked.connect(self.to_add_progressbar)
# Центруем и меняем размеры окна под экран, на котором находится
# указатель мыши (необходимо для Linux и Windows не портит)
selected_screen = probe_cursor_screen()
current_geo = selected_screen.availableGeometry()
self.move(current_geo.x() + current_geo.width() // 4,
current_geo.y() + current_geo.height() // 4)
self.resize(current_geo.width() // 2,
current_geo.height() // 2)
def to_add_progressbar(self):
pbar = QProgressBar(self)
pbar.setRange(0, random.randint(1, 10)*10)
pbar.valueChanged.connect(self.to_pbar_valuechanged)
pbar.setValue(0)
self.central_widget.vbox.addWidget(pbar)
def to_pbar_valuechanged(self, value):
def remove_pbar(pbar):
self.central_widget.vbox.removeWidget(pbar)
pbar.deleteLater()
if (value < self.sender().maximum()):
if not Timer_with_int_transit_value.single_shot(
self, random.choice([100,150,200]),
self.sender().setValue, value+1):
remove_pbar(self.sender())
QMessageBox.warning(
self, "О Ш И Б К А !!!",
"Недопустимый тип параметра при вызове\n"
"Timer_with_int_transit_value.single_shot()."
)
else:
remove_pbar(self.sender())
if __name__ == "__main__":
app = QApplication(sys.argv)
mwin = Probe_main_window()
mwin.show()
sys.exit(app.exec_()) |
|
Обновлено 01.04.2022 20:52
Вот и выявилась недоработка в имитаторе. Как-то не учёл тот факт, что пока не закрыты все индикаторы хода выполнения QProgressBar, один экземпляр самописного таймера на базе QThread всегда находится в активном состоянии. Соответственно, закрытие приложения нажатием на значок в полосе заголовка в момент, когда отработали не все индикаторы хода выполнения, приводило к выдаче сообщения
| Code | 1
| QThread: Destroyed while thread is still running |
|
Но это только под Windows. А Linux молчал, что не оправдывает упущение, конечно.
Изменения внесены. Обновлённая версия ниже, предыдущая убрана под спойлер для истории.
Версия имитатора убрана под спойлер для истории
Кликните здесь для просмотра всего текста
Имитатор:
| 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
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
| #!/usr/bin/python3
# -*- coding: utf-8 -*-
"""Dynamic Central Probe v0.003
Добавление и удаление дочерних виджетов в центральный виджет
по запросу пользователя (имитатор).
"""
import sys, copy, random
from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR
from PyQt5.QtGui import QCursor
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import QMutex, QMutexLocker
from PyQt5.QtCore import QObject, QTimer, QThread
from PyQt5.QtWidgets import QApplication, QMessageBox
from PyQt5.QtWidgets import QWidget, QPushButton
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QMainWindow, QProgressBar
APP_NAME = "Dynamic Central Probe v0.003"
class Timer_with_int_transit_value(QThread):
thread_mutex = QMutex()
timeout_value = 0
receiver_method = None
transit_value = 0
transit_signal = pyqtSignal(int)
def set_timeout_value(self, value):
""" для наружного применения
value - значение типа int
задержка отправки сигнала transit_signal в милисекундах
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.timeout_value = copy.copy(value)
if type(self.timeout_value) == int:
rc = True
return rc
def set_transit_value(self, value):
""" для наружного применения
value - значение типа int
значение, отправляемое получателю с помощью сигнала transit_signal
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.transit_value = copy.copy(value)
if type(self.transit_value) == int:
rc = True
return rc
def set_receiver_method(self, value):
""" для наружного применения
value - значение типа builtin_function_or_method
функция или метод - получатель сигнала transit_signal
"""
rc = False
with QMutexLocker(self.thread_mutex):
self.receiver_method = copy.copy(value)
if (str(type(self.receiver_method)) ==
"<class 'builtin_function_or_method'>"):
rc = True
return rc
def get_timeout_value(self):
""" для внутреннего применения
возвращает значение типа int
задержка отправки сигнала transit_signal в милисекундах
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.timeout_value)
return rc
def get_transit_value(self):
""" для внутреннего применения
возвращает значение типа int
значение, отправляемое получателю с помощью сигнала transit_signal
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.transit_value)
return rc
def get_receiver_method(self):
""" для внутреннего применения
возвращает значение типа builtin_function_or_method
функция или метод - получатель сигнала transit_signal
"""
with QMutexLocker(self.thread_mutex):
rc = copy.copy(self.receiver_method)
return rc
def run(self):
""" для внутреннего применения
выполняется в дополнительном потоке после вызова QThread.start()
"""
self.transit_signal.connect(self.get_receiver_method())
QThread.msleep(self.get_timeout_value())
self.transit_signal.emit(self.get_transit_value())
def single_shot(client, timeout_value, receiver_method, transit_value):
""" для наружного применения
подобие QTimer.singleShot(timeout_value, receiver_method)
"""
thread = Timer_with_int_transit_value(client)
if (thread.set_timeout_value(timeout_value) and
thread.set_receiver_method(receiver_method) and
thread.set_transit_value(transit_value)):
thread.finished.connect(thread.deleteLater)
thread.start()
return True
else:
thread.deleteLater()
#del thread
return False
class Probe_main_window(QMainWindow):
"""Главное окно приложения
"""
is_shutdown_requested = False
def __init__(self, parent=None):
super(Probe_main_window, self).__init__(parent)
self.init_ui()
def init_ui(self):
def probe_cursor_screen():
"""Определяем экран, на котором находится указатель мыши.
Необходимо для Linux и Windows не портит.
"""
selected_screen = QApplication.screens()[0]
if (int(QT_VERSION_STR.split('.')[1]) >= 10 and
int(PYQT_VERSION_STR.split('.')[1]) >= 10):
selected_screen = QApplication.screenAt(QCursor.pos())
else:
screens = QApplication.screens()
if len(screens) > 0:
for i in range(len(screens)):
if screens[i].geometry().contains(QCursor.pos()):
selected_screen = screens[i]
break
return selected_screen
self.setWindowTitle(APP_NAME)
self.statusBar()
self.central_widget = QWidget(self)
add_button = QPushButton("Пуск", self)
hbox = QHBoxLayout()
hbox.addWidget(add_button)
hbox.addStretch(1)
self.central_widget.vbox = QVBoxLayout()
main_box = QVBoxLayout()
main_box.addLayout(self.central_widget.vbox)
main_box.addStretch(1)
main_box.addLayout(hbox)
self.central_widget.setLayout(main_box)
self.setCentralWidget(self.central_widget)
add_button.clicked.connect(self.to_add_progressbar)
# Центруем и меняем размеры окна под экран, на котором находится
# указатель мыши (необходимо для Linux и Windows не портит)
selected_screen = probe_cursor_screen()
current_geo = selected_screen.availableGeometry()
self.move(current_geo.x() + current_geo.width() // 4,
current_geo.y() + current_geo.height() // 4)
self.resize(current_geo.width() // 2,
current_geo.height() // 2)
def to_add_progressbar(self):
pbar = QProgressBar(self)
pbar.setRange(0, random.randint(1, 10)*10)
pbar.valueChanged.connect(self.to_pbar_valuechanged)
pbar.setValue(0)
self.central_widget.vbox.addWidget(pbar)
def to_pbar_valuechanged(self, value):
def remove_pbar(pbar):
self.central_widget.vbox.removeWidget(pbar)
pbar.deleteLater()
if (value < self.sender().maximum() and
(not self.is_shutdown_requested)):
pass
if not Timer_with_int_transit_value.single_shot(
self, random.choice([100,150,200]),
self.sender().setValue, value+1):
remove_pbar(self.sender())
QMessageBox.warning(
self, "О Ш И Б К А !!!",
"Недопустимый тип параметра при вызове\n"
"Timer_with_int_transit_value.single_shot()."
)
else:
remove_pbar(self.sender())
def closeEvent(self, e):
if not self.is_shutdown_requested:
self.is_shutdown_requested = True
e.ignore()
QTimer.singleShot(500, self.close)
# отложенный повтор закрытия приложения,
# чтобы самописный таймер успел отработать
else:
e.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
mwin = Probe_main_window()
mwin.show()
sys.exit(app.exec_()) |
|
Обновлено 04.04.2022 14:27
Ндааа... "На третий день Зоркий Глаз заметил, что..." Ну, ладно, не Зоркий Глаз и не на третий, а на четвёртый, но всё-таки заметил, что имитатору ни какой особый самописный таймер не нужен... Инерция мышления - коварная штука Так и запишем в свой дневничок...
Изменения внесены. Обновлённая версия ниже, предыдущая убрана под спойлер для истории.
Имитатор:
| 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
| #!/usr/bin/python3
# -*- coding: utf-8 -*-
"""Dynamic Central Probe v0.004
Добавление и удаление дочерних виджетов в центральный виджет
по запросу пользователя (имитатор).
"""
import sys, random
from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR
from PyQt5.QtGui import QCursor
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QWidget, QPushButton
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QMainWindow, QProgressBar
APP_NAME = "Dynamic Central Probe v0.004"
class Progressbar_for_imitator(QProgressBar):
def to_next_value(self):
self.setValue(self.value() + 1)
class Probe_main_window(QMainWindow):
"""Главное окно приложения
"""
def __init__(self, parent=None):
super(Probe_main_window, self).__init__(parent)
self.init_ui()
def init_ui(self):
def probe_cursor_screen():
"""Определяем экран, на котором находится указатель мыши.
Необходимо для Linux и Windows не портит.
"""
selected_screen = QApplication.screens()[0]
if (int(QT_VERSION_STR.split('.')[1]) >= 10 and
int(PYQT_VERSION_STR.split('.')[1]) >= 10):
selected_screen = QApplication.screenAt(QCursor.pos())
else:
screens = QApplication.screens()
if len(screens) > 0:
for i in range(len(screens)):
if screens[i].geometry().contains(QCursor.pos()):
selected_screen = screens[i]
break
return selected_screen
self.setWindowTitle(APP_NAME)
self.statusBar()
self.central_widget = QWidget(self)
add_button = QPushButton("Пуск", self)
hbox = QHBoxLayout()
hbox.addWidget(add_button)
hbox.addStretch(1)
self.central_widget.vbox = QVBoxLayout()
main_box = QVBoxLayout()
main_box.addLayout(self.central_widget.vbox)
main_box.addStretch(1)
main_box.addLayout(hbox)
self.central_widget.setLayout(main_box)
self.setCentralWidget(self.central_widget)
add_button.clicked.connect(self.to_add_progressbar)
# Центруем и меняем размеры окна под экран, на котором находится
# указатель мыши (необходимо для Linux и Windows не портит)
selected_screen = probe_cursor_screen()
current_geo = selected_screen.availableGeometry()
self.move(current_geo.x() + current_geo.width() // 4,
current_geo.y() + current_geo.height() // 4)
self.resize(current_geo.width() // 2,
current_geo.height() // 2)
def to_add_progressbar(self):
pbar = Progressbar_for_imitator(self)
pbar.setRange(0, random.randint(1, 10)*10)
pbar.valueChanged.connect(self.to_pbar_valuechanged)
pbar.setValue(0)
self.central_widget.vbox.addWidget(pbar)
def to_pbar_valuechanged(self, value):
if value < self.sender().maximum():
QTimer.singleShot(random.choice([100,150,200]),
self.sender().to_next_value)
else:
self.central_widget.vbox.removeWidget(self.sender())
self.sender().deleteLater()
if __name__ == "__main__":
app = QApplication(sys.argv)
mwin = Probe_main_window()
mwin.show()
sys.exit(app.exec_()) |
|
|