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

К вопросу о симуляторах и имитаторах...

Запись от iamvic размещена 01.04.2022 в 00:00
Показов 1841 Комментарии 0

Забавно, что по иным причинам, но именно к первому апреля пришлось приводить к товарному виду пару своих демонстрашек, динамически размещающих и удаляющих индикаторы QProgressBar в центральном виджете главного окна.

В первом случае каждое нажатие кнопки Пуск приводит к реальному запуску в дополнительном потоке нового исполнителя, который симулирует бурную деятельность (проще говоря, спит), периодически информируя пользователя о якобы проделанной работе отправкой сигнала соответствующему индикатору QProgressBar.

Во втором случае никто такими благоглупостями заниматься не собирается. Зачем нам исполнитель, если его деятельность можно вполне успешно имитировать? Ничто не мешает в ответ на получение сигнала valueChanged от индикатора QProgressBar посылать свой сигнал с новым значением хода выполнения с некоторой задержкой по времени, используя специально обученый таймер.

А, впрочем, какая разница? Симулятор или имитатор - результат-то налицо

Нажмите на изображение для увеличения
Название: image.png
Просмотров: 449
Размер:	13.0 Кб
ID:	7436

Симулятор:
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_())
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть не большие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru