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

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

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

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

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

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

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

Нажмите на изображение для увеличения
Название: image.png
Просмотров: 445
Размер:	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
Комментарии
 
Новые блоги и статьи
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
Саморегулирующийся социальный контракт для сервера 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 Текстовая версия
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru