Skip to content

Commit 3c40af0

Browse files
authored
[GUI] show active window after minimizing (#142)
* cleanup and comment * move fct back to base * move test * removed unused imports * missing * first working version of working minimize * fixing call order * cleanup * minor * cleanup and adding tests * adding another test * adding description * fixing issues * adding tests * missing import
1 parent f4eb1f0 commit 3c40af0

12 files changed

+174
-52
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# _ __ _ ___ _ ___ _ _
2+
# | |/ /_ _ __ _| |_ ___ __/ __| __ _| |___ _ __ ___| _ \ |_ _ __ _(_)_ _
3+
# | ' <| '_/ _` | _/ _ (_-<__ \/ _` | / _ \ ' \/ -_) _/ | || / _` | | ' \
4+
# |_|\_\_| \__,_|\__\___/__/___/\__,_|_\___/_|_|_\___|_| |_|\_,_\__, |_|_||_|
5+
# |___/
6+
# License: BSD License ; see LICENSE
7+
#
8+
# Main authors: Philipp Bucher (https://github.com/philbucher)
9+
#
10+
11+
"""
12+
The currently active window is saved in a global variable
13+
This is necessary in case the window gets minimized
14+
I.e. when the plugin is reopened (with a previously minimized window)
15+
then the last opened window is shown again instead of creating again the base window
16+
"""
17+
18+
ACTIVE_WINDOW=None

kratos_salome_plugin/gui/base_window.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,22 @@
1616

1717
# qt imports
1818
from PyQt5.QtWidgets import QMainWindow
19-
from PyQt5.QtCore import Qt, QTimer
19+
from PyQt5.QtCore import Qt, QTimer, QEvent
2020
from PyQt5.QtGui import QIcon
2121
from PyQt5 import uic
2222

2323
# plugin imports
24+
import kratos_salome_plugin.gui.active_window as active_window
2425
from kratos_salome_plugin.utilities import GetAbsPathInPlugin
2526
from kratos_salome_plugin.utilities import PathCheck
2627

2728

2829
class BaseWindow(QMainWindow):
2930
def __init__(self, ui_form_path, parent=None):
30-
logger.debug('Creating BaseWindow')
31-
3231
super().__init__()
3332

33+
logger.debug('Creating %s', self.__class__.__name__)
34+
3435
PathCheck(ui_form_path)
3536
self.__InitUI(ui_form_path)
3637

@@ -39,6 +40,14 @@ def __init__(self, ui_form_path, parent=None):
3940
if self.parent:
4041
self.parent.hide()
4142

43+
def ShowOnTop(self) -> None:
44+
"""show and activate the window, works both if opened newly or minimized
45+
see https://kb.froglogic.com/squish/qt/howto/maximizing-minimizing-restoring-resizing-positioning-windows/
46+
"""
47+
self.show()
48+
self.activateWindow()
49+
self.setWindowState(Qt.WindowNoState)
50+
4251
def StatusBarInfo(self, message: str, msg_time: int=10) -> None:
4352
"""show an info message in the statusbar
4453
input for time is in seconds
@@ -64,8 +73,21 @@ def closeEvent(self, event):
6473
if self.parent:
6574
self.parent.show()
6675

76+
# resetting the global var to not accidentially keep a closed window alive
77+
# this could happen if the window was minimized at some point
78+
active_window.ACTIVE_WINDOW = None
79+
6780
super().closeEvent(event)
6881

82+
def changeEvent(self, event):
83+
if event.type() == QEvent.WindowStateChange:
84+
if self.windowState() & Qt.WindowMinimized:
85+
# saving the currently active window such that it can be maximized again
86+
# when the plugin is re-opened in salome
87+
active_window.ACTIVE_WINDOW = self
88+
89+
super().changeEvent(event)
90+
6991
def __InitUI(self, ui_form_path) -> None:
7092
"""initialize the user interface from the "ui" file
7193
also set some settings that cannot be specified through the "ui" file

kratos_salome_plugin/gui/groups_window.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
class GroupsWindow(BaseWindow):
2525
def __init__(self, parent):
26-
logger.debug('Creating GroupsWindow')
2726
super().__init__(Path(GetAbsPathInPlugin("gui", "ui_forms", "groups_window.ui")), parent)
2827

2928

kratos_salome_plugin/gui/plugin_controller.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from kratos_salome_plugin.exceptions import UserInputError
2424
from kratos_salome_plugin.gui.plugin_main_window import PluginMainWindow
2525
from kratos_salome_plugin.gui.about import ShowAbout
26+
import kratos_salome_plugin.gui.active_window as active_window
2627
from kratos_salome_plugin.gui.project_manager import ProjectManager
2728
from kratos_salome_plugin.gui.project_path_handler import ProjectPathHandler
2829

@@ -35,22 +36,19 @@ class PluginController:
3536
def __init__(self):
3637
logger.debug('Creating PluginController')
3738
self._main_window = PluginMainWindow()
39+
active_window.ACTIVE_WINDOW = self._main_window
40+
3841
self.__InitializeMembers()
3942

4043
self.__ConnectMainWindow()
4144

42-
def ShowMainWindow(self) -> None:
43-
"""show main window"""
44-
self._main_window.ShowOnTop()
45-
4645

4746
def __InitializeMembers(self) -> None:
4847
"""completely reinitialize members to clean them"""
4948
self._project_manager = ProjectManager()
5049
self._project_path_handler = ProjectPathHandler()
5150
self._previous_save_path = None
5251

53-
5452
def __ConnectMainWindow(self) -> None:
5553
### File menu
5654
self._main_window.actionNew.triggered.connect(self._New)

kratos_salome_plugin/gui/plugin_main_window.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,24 @@
1616
import logging
1717
logger = logging.getLogger(__name__)
1818

19-
# qt imports
20-
from PyQt5.QtCore import Qt
21-
2219
# plugin imports
2320
from kratos_salome_plugin.utilities import GetAbsPathInPlugin
2421
from kratos_salome_plugin.gui.base_window import BaseWindow
22+
import kratos_salome_plugin.gui.active_window as active_window
2523

2624

2725
class PluginMainWindow(BaseWindow):
2826
def __init__(self):
29-
logger.debug('Creating PluginMainWindow')
3027
super().__init__(Path(GetAbsPathInPlugin("gui", "ui_forms", "plugin_main_window.ui")))
3128

32-
def ShowOnTop(self) -> None:
33-
"""show and activate the window, works both if opened newly or minimized
34-
see https://kb.froglogic.com/squish/qt/howto/maximizing-minimizing-restoring-resizing-positioning-windows/
35-
"""
36-
self.show()
37-
self.activateWindow()
38-
self.setWindowState(Qt.WindowNoState)
39-
4029
def closeEvent(self, event):
4130
"""prevent the window from closing, only hiding it
4231
Note that this deliberately does not call the baseclass, as the event should be ignored
4332
"""
33+
# making this window the active one so that it can be reopened
34+
# needed when reopening the plugin in salome
35+
active_window.ACTIVE_WINDOW = self
36+
4437
event.ignore()
4538
self.hide()
4639

kratos_salome_plugin/reload_modules.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"base_application",
4343
"gui.utilities",
4444
"gui.about",
45+
"gui.active_window",
4546
"gui.project_path_handler",
4647
"gui.project_manager",
4748
"gui.base_window",

salome_plugins.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def InitializePlugin(context):
2828

2929
# plugin imports
3030
from kratos_salome_plugin.gui.plugin_controller import PluginController
31+
import kratos_salome_plugin.gui.active_window as active_window
3132
import kratos_salome_plugin.version as plugin_version
3233
from kratos_salome_plugin import salome_utilities
3334
from kratos_salome_plugin.reload_modules import ReloadModules
@@ -71,7 +72,7 @@ def InitializePlugin(context):
7172
# initialize only once the PluginController
7273
PLUGIN_CONTROLLER = PluginController()
7374

74-
PLUGIN_CONTROLLER.ShowMainWindow()
75+
active_window.ACTIVE_WINDOW.ShowOnTop()
7576

7677
logger.info("Successfully initialized plugin")
7778

tests/test_base_window.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
# plugin imports
1919
from kratos_salome_plugin.gui.base_window import BaseWindow
20+
import kratos_salome_plugin.gui.active_window as active_window
2021

2122
# tests imports
2223
from testing_utilities import QtTestCase, GetTestsPath
@@ -59,6 +60,33 @@ def test_close_esc(self):
5960
self.assertEqual(path_close_event.call_count, 1)
6061

6162

63+
class TestBaseWindowWindowStates(QtTestCase):
64+
"""This test makes sure the window shows up again after being minimized"""
65+
def test_minimize(self):
66+
window = BaseWindow(ui_file)
67+
self.assertTrue(window.isHidden())
68+
69+
window.ShowOnTop()
70+
71+
window.setWindowState(Qt.WindowMinimized)
72+
73+
self.assertFalse(window.isActiveWindow())
74+
self.assertTrue(window.isMinimized())
75+
self.assertTrue(window.isVisible())
76+
self.assertFalse(window.isHidden())
77+
self.assertEqual(window.windowState(), Qt.WindowMinimized)
78+
79+
window.ShowOnTop()
80+
81+
# self.assertTrue(window.isActiveWindow()) # commented as doesn't work in the CI and in Linux, seems OS dependent
82+
self.assertFalse(window.isMinimized())
83+
self.assertTrue(window.isVisible())
84+
self.assertFalse(window.isHidden())
85+
self.assertEqual(window.windowState(), Qt.WindowNoState)
86+
87+
window.close()
88+
89+
6290
class TestBaseWindowStatusBar(QtTestCase):
6391
def test_StatusBarInfo(self):
6492
window = BaseWindow(ui_file)
@@ -94,5 +122,42 @@ def test_hide_show_parent(self):
94122
self.assertTrue(parent_window.isVisible())
95123

96124

125+
class TestBaseWindowMinimize_ActiveWindow(QtTestCase):
126+
def setUp(self):
127+
# setting initial state
128+
active_window.ACTIVE_WINDOW = None
129+
130+
def test_set_active_window(self):
131+
window = BaseWindow(ui_file)
132+
window.show()
133+
134+
window.setWindowState(Qt.WindowMinimized)
135+
136+
self.assertIs(active_window.ACTIVE_WINDOW, window)
137+
138+
def test_set_active_window_parent(self):
139+
parent_window = BaseWindow(ui_file)
140+
parent_window.show()
141+
142+
window = BaseWindow(ui_file, parent_window)
143+
144+
window.setWindowState(Qt.WindowMinimized)
145+
146+
self.assertIs(active_window.ACTIVE_WINDOW, window)
147+
148+
def test_set_active_window_reset(self):
149+
parent_window = BaseWindow(ui_file)
150+
parent_window.show()
151+
152+
window = BaseWindow(ui_file, parent_window)
153+
154+
window.setWindowState(Qt.WindowMinimized)
155+
156+
window.show()
157+
window.close()
158+
159+
self.assertIsNone(active_window.ACTIVE_WINDOW) # make sure resettign the global var works
160+
161+
97162
if __name__ == '__main__':
98163
unittest.main()

tests/test_plugin_controller.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
# plugin imports
2121
from kratos_salome_plugin.gui.plugin_controller import PluginController
22+
import kratos_salome_plugin.gui.active_window as active_window
2223

2324
# tests imports
2425
from testing_utilities import QtTestCase, CreateHDFStudyFile, DeleteDirectoryIfExisting, SalomeTestCaseWithBox, skipUnlessPythonVersionIsAtLeast
@@ -115,7 +116,7 @@ def test_help_website(self):
115116
self.assertEqual(patch_fct.open.call_count, 1)
116117

117118

118-
class TestPluginControllerWindowCloseReopen(QtTestCase):
119+
class TestPluginControllerMainWindowCloseReopen(QtTestCase):
119120
"""This test makes sure if the MainWindow is closed, it is not destroyed"""
120121

121122
def test_main_window_reopen(self):
@@ -125,11 +126,21 @@ def test_main_window_reopen(self):
125126

126127
controller._main_window.close()
127128

128-
controller.ShowMainWindow()
129+
controller._main_window.ShowOnTop()
129130

130131
self.assertIs(orig_obj, controller._main_window)
131132

132133

134+
class TestPluginControllerMainWindow_ActiveWindow(QtTestCase):
135+
def test_main_window_active_window(self):
136+
# setting initial state
137+
active_window.ACTIVE_WINDOW = None
138+
139+
controller = PluginController()
140+
141+
self.assertIs(active_window.ACTIVE_WINDOW, controller._main_window)
142+
143+
133144
# using a module local patch due to import of QFileDialog in project_path_handler
134145
# see https://realpython.com/python-mock-library/#where-to-patch
135146
_QFileDialog_patch = 'kratos_salome_plugin.gui.project_path_handler.QFileDialog.'
@@ -155,7 +166,7 @@ def test_New(self):
155166

156167
def test_Close(self):
157168
controller = PluginController()
158-
controller.ShowMainWindow()
169+
controller._main_window.ShowOnTop()
159170

160171
self.assertFalse(controller._main_window.isMinimized())
161172
self.assertTrue(controller._main_window.isVisible())

tests/test_plugin_main_window.py

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
# plugin imports
1919
from kratos_salome_plugin.gui.plugin_main_window import PluginMainWindow
20+
import kratos_salome_plugin.gui.active_window as active_window
2021

2122
# tests imports
2223
from testing_utilities import QtTestCase
@@ -117,31 +118,16 @@ def __CheckMockCalls(self, called_mock, exp_call_count=1):
117118
self.assertFalse(self.mocks[mock_name].called, msg='Unexpected call for mock "{}": "{}"'.format(called_mock, mock_name))
118119

119120

120-
class TestPluginMainWindowWindowStates(QtTestCase):
121-
"""This test makes sure the window shows up again after being minimized"""
122-
def test_minimize(self):
123-
main_window = PluginMainWindow()
124-
self.assertTrue(main_window.isHidden())
121+
class TestPluginMainWindow_ActiveWindow(QtTestCase):
122+
def test_set_active_window(self):
123+
active_window.ACTIVE_WINDOW = None
125124

126-
main_window.ShowOnTop()
125+
window = PluginMainWindow()
126+
window.show()
127+
window.close()
127128

128-
main_window.setWindowState(Qt.WindowMinimized)
129-
130-
self.assertFalse(main_window.isActiveWindow())
131-
self.assertTrue(main_window.isMinimized())
132-
self.assertTrue(main_window.isVisible())
133-
self.assertFalse(main_window.isHidden())
134-
self.assertEqual(main_window.windowState(), Qt.WindowMinimized)
135-
136-
main_window.ShowOnTop()
137-
138-
# self.assertTrue(main_window.isActiveWindow()) # commented as doesn't work in the CI and in Linux, seems OS dependent
139-
self.assertFalse(main_window.isMinimized())
140-
self.assertTrue(main_window.isVisible())
141-
self.assertFalse(main_window.isHidden())
142-
self.assertEqual(main_window.windowState(), Qt.WindowNoState)
143-
144-
main_window.close()
129+
# make sure the main win is saved as active when closing it so that it can be reopened
130+
self.assertIs(active_window.ACTIVE_WINDOW, window)
145131

146132

147133
if __name__ == '__main__':

0 commit comments

Comments
 (0)