FreeCAD

Форк
0
147 строк · 6.1 Кб
1
# SPDX-License-Identifier: LGPL-2.1-or-later
2
# ***************************************************************************
3
# *                                                                         *
4
# *   Copyright (c) 2022-2023 FreeCAD Project Association                   *
5
# *                                                                         *
6
# *   This file is part of FreeCAD.                                         *
7
# *                                                                         *
8
# *   FreeCAD is free software: you can redistribute it and/or modify it    *
9
# *   under the terms of the GNU Lesser General Public License as           *
10
# *   published by the Free Software Foundation, either version 2.1 of the  *
11
# *   License, or (at your option) any later version.                       *
12
# *                                                                         *
13
# *   FreeCAD is distributed in the hope that it will be useful, but        *
14
# *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
15
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
16
# *   Lesser General Public License for more details.                       *
17
# *                                                                         *
18
# *   You should have received a copy of the GNU Lesser General Public      *
19
# *   License along with FreeCAD. If not, see                               *
20
# *   <https://www.gnu.org/licenses/>.                                      *
21
# *                                                                         *
22
# ***************************************************************************
23

24
from PySide import QtCore, QtWidgets
25

26
from AddonManagerTest.app.mocks import SignalCatcher
27

28

29
class DialogInteractor(QtCore.QObject):
30
    """Takes the title of the dialog and a callable. The callable is passed the widget
31
    we found and can do whatever it wants to it. Whatever it does should eventually
32
    close the dialog, however."""
33

34
    def __init__(self, dialog_to_watch_for, interaction):
35
        super().__init__()
36

37
        # Status variables for tests to check:
38
        self.dialog_found = False
39
        self.has_run = False
40
        self.button_found = False
41
        self.interaction = interaction
42

43
        self.dialog_to_watch_for = dialog_to_watch_for
44

45
        self.execution_counter = 0
46
        self.timer = QtCore.QTimer()
47
        self.timer.timeout.connect(self.run)
48
        self.timer.start(
49
            1
50
        )  # At 10 this occasionally left open dialogs; less than 1 produced failed tests
51

52
    def run(self):
53
        widget = QtWidgets.QApplication.activeModalWidget()
54
        if widget and self._dialog_matches(widget):
55
            # Found the dialog we are looking for: now try to run the interaction
56
            if self.interaction is not None and callable(self.interaction):
57
                self.interaction(widget)
58
            self.dialog_found = True
59
            self.timer.stop()
60

61
        self.has_run = True
62
        self.execution_counter += 1
63
        if self.execution_counter > 100:
64
            print("Stopped timer after 100 iterations")
65
            self.timer.stop()
66

67
    def _dialog_matches(self, widget) -> bool:
68
        # Is this the widget we are looking for? Only applies on Linux and Windows: macOS
69
        # doesn't set the title of a modal dialog:
70
        os = QtCore.QSysInfo.productType()  # Qt5 gives "osx", Qt6 gives "macos"
71
        if os in ["osx", "macos"] or (
72
            hasattr(widget, "windowTitle")
73
            and callable(widget.windowTitle)
74
            and widget.windowTitle() == self.dialog_to_watch_for
75
        ):
76
            return True
77
        return False
78

79

80
class DialogWatcher(DialogInteractor):
81
    """Examine the running GUI and look for a modal dialog with a given title, containing a button
82
    with a role. Click that button, which is expected to close the dialog. Generally run on
83
    a one-shot QTimer to allow the dialog time to open up. If the specified dialog is found, but
84
    it does not contain the expected button, button_found will be false, and the dialog will be
85
    closed with a reject() slot."""
86

87
    def __init__(self, dialog_to_watch_for, button=QtWidgets.QDialogButtonBox.NoButton):
88
        super().__init__(dialog_to_watch_for, self.click_button)
89
        if button != QtWidgets.QDialogButtonBox.NoButton:
90
            self.button = button
91
        else:
92
            self.button = QtWidgets.QDialogButtonBox.Cancel
93

94
    def click_button(self, widget):
95
        button_boxes = widget.findChildren(QtWidgets.QDialogButtonBox)
96
        if len(button_boxes) == 1:  # There should be one, and only one
97
            button_to_click = button_boxes[0].button(self.button)
98
            if button_to_click:
99
                self.button_found = True
100
                button_to_click.click()
101
            else:
102
                widget.reject()
103
        else:
104
            widget.reject()
105

106

107
class FakeWorker:
108
    def __init__(self):
109
        self.called = False
110
        self.should_continue = True
111

112
    def work(self):
113
        while self.should_continue:
114
            QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 100)
115

116
    def stop(self):
117
        self.should_continue = False
118

119

120
class MockThread:
121
    def wait(self):
122
        pass
123

124
    def isRunning(self):
125
        return False
126

127

128
class AsynchronousMonitor:
129
    """Watch for a signal to be emitted for at most some given number of milliseconds"""
130

131
    def __init__(self, signal):
132
        self.signal = signal
133
        self.signal_catcher = SignalCatcher()
134
        self.signal.connect(self.signal_catcher.catch_signal)
135
        self.kill_timer = QtCore.QTimer()
136
        self.kill_timer.setSingleShot(True)
137
        self.kill_timer.timeout.connect(self.signal_catcher.die)
138

139
    def wait_for_at_most(self, max_wait_millis) -> None:
140
        self.kill_timer.setInterval(max_wait_millis)
141
        self.kill_timer.start()
142
        while not self.signal_catcher.caught and not self.signal_catcher.killed:
143
            QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 10)
144
        self.kill_timer.stop()
145

146
    def good(self) -> bool:
147
        return self.signal_catcher.caught and not self.signal_catcher.killed
148

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.