Skip to content

Commit f10ec7d

Browse files
committed
Refactor ThemeManager:
- Improve thread safety with lock - Keep 1 reference to manager - Remove all imports the manager (except 1) - Added logging if manager is not available, or themes are not found - This fixes a freeze in Windows on some systems during the launch of OpenShot, related to oleaut32.dll.
1 parent 891421f commit f10ec7d

File tree

8 files changed

+83
-68
lines changed

8 files changed

+83
-68
lines changed

src/classes/app.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def __init__(self, *args, **kwargs):
153153

154154
# Instantiate Theme Manager (Singleton)
155155
from themes.manager import ThemeManager
156-
ThemeManager(self)
156+
self.theme_manager = ThemeManager(self)
157157

158158
def show_environment(self, info, openshot):
159159
log = self.log
@@ -256,9 +256,8 @@ def gui(self):
256256
self.window = MainWindow()
257257

258258
# Instantiate Theme Manager (Singleton)
259-
from themes.manager import ThemeManager, ThemeName
260-
theme_enum = ThemeName.find_by_name(self.settings.get("theme"))
261-
theme = ThemeManager().apply_theme(theme_enum)
259+
theme_name = self.settings.get("theme")
260+
theme = self.theme_manager.apply_theme(theme_name)
262261

263262
# Update theme in settings
264263
self.settings.set("theme", theme.name)

src/themes/base.py

+11-13
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434

3535
from classes import ui_util
3636
from classes.info import PATH
37-
from themes.manager import ThemeManager
3837

3938

4039
class BaseTheme:
@@ -150,26 +149,25 @@ def set_toolbar_buttons(self, toolbar, icon_size=24, settings=None):
150149
if button_stylesheet:
151150
button.setStyleSheet(button_stylesheet)
152151

153-
154152
def apply_theme(self):
155-
# Get initial style and palette
156-
manager = ThemeManager()
157-
158153
# Apply the stylesheet to the entire application
159-
if manager.original_style:
160-
self.app.setStyle(manager.original_style)
161-
if manager.original_palette:
162-
self.app.setPalette(manager.original_palette)
154+
from classes import info
155+
from classes.logger import log
156+
from PyQt5.QtGui import QFont, QFontDatabase
157+
158+
if not self.app.theme_manager:
159+
log.warning("ThemeManager not initialized yet. Skip applying a theme.")
160+
161+
if self.app.theme_manager.original_style:
162+
self.app.setStyle(self.app.theme_manager.original_style)
163+
if self.app.theme_manager.original_palette:
164+
self.app.setPalette(self.app.theme_manager.original_palette)
163165
self.app.setStyleSheet(self.style_sheet)
164166

165167
# Hide main window status bar
166168
if hasattr(self.app, "window") and hasattr(self.app.window, "statusBar"):
167169
self.app.window.statusBar.hide()
168170

169-
from classes import info
170-
from classes.logger import log
171-
from PyQt5.QtGui import QFont, QFontDatabase
172-
173171
# Load embedded font
174172
font_path = os.path.join(info.IMAGES_PATH, "fonts", "Ubuntu-R.ttf")
175173
if os.path.exists(font_path):

src/themes/manager.py

+16-10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
2626
"""
2727

28+
import threading
2829
from enum import Enum
2930

3031

@@ -51,31 +52,36 @@ def find_by_name(name):
5152
class ThemeManager:
5253
"""Singleton Theme Manager class, used to easily switch between UI themes"""
5354
_instance = None
55+
_lock = threading.Lock()
5456

5557
def __new__(cls, app=None):
5658
"""Override new method, so the same instance is always returned (i.e. singleton)"""
5759
if cls._instance is None:
58-
cls._instance = super(ThemeManager, cls).__new__(cls)
59-
cls._instance.app = app
60-
cls._instance.original_style = app.style().objectName() if app else None
61-
cls._instance.original_palette = app.palette() if app else None
62-
cls._instance.current_theme = None
60+
with cls._lock:
61+
if cls._instance is None:
62+
cls._instance = super(ThemeManager, cls).__new__(cls)
63+
cls._instance.app = app
64+
cls._instance.original_style = app.style().objectName() if app else None
65+
cls._instance.original_palette = app.palette() if app else None
66+
cls._instance.current_theme = None
6367
return cls._instance
6468

65-
def apply_theme(self, theme_name):
69+
def apply_theme(self, name):
6670
"""Apply a new UI theme. Expects a ThemeName ENUM as the arg."""
67-
if theme_name == ThemeName.HUMANITY_DARK:
71+
theme_enum = ThemeName.find_by_name(name)
72+
73+
if theme_enum == ThemeName.HUMANITY_DARK:
6874
from themes.humanity.theme import HumanityDarkTheme
6975
self.current_theme = HumanityDarkTheme(self.app)
70-
elif theme_name == ThemeName.RETRO:
76+
elif theme_enum == ThemeName.RETRO:
7177
from themes.humanity.theme import Retro
7278
self.current_theme = Retro(self.app)
73-
elif theme_name == ThemeName.COSMIC:
79+
elif theme_enum == ThemeName.COSMIC:
7480
from themes.cosmic.theme import CosmicTheme
7581
self.current_theme = CosmicTheme(self.app)
7682

7783
# Set name on theme instance
78-
self.current_theme.name = theme_name.value
84+
self.current_theme.name = theme_enum.value
7985

8086
# Apply theme
8187
self.current_theme.apply_theme()

src/windows/main_window.py

+24-21
Original file line numberDiff line numberDiff line change
@@ -1134,10 +1134,10 @@ def onPlayCallback(self):
11341134
"""Handle when playback is started"""
11351135
# Set icon on Play button
11361136
if self.initialized:
1137-
from themes.manager import ThemeManager
1138-
theme = ThemeManager().get_current_theme()
1139-
if theme:
1140-
theme.togglePlayIcon(True)
1137+
if get_app().theme_manager:
1138+
theme = get_app().theme_manager.get_current_theme()
1139+
if theme:
1140+
theme.togglePlayIcon(True)
11411141

11421142
def onPauseCallback(self):
11431143
"""Handle when playback is paused"""
@@ -1146,10 +1146,10 @@ def onPauseCallback(self):
11461146

11471147
# Set icon on Pause button
11481148
if self.initialized:
1149-
from themes.manager import ThemeManager
1150-
theme = ThemeManager().get_current_theme()
1151-
if theme:
1152-
theme.togglePlayIcon(False)
1149+
if get_app().theme_manager:
1150+
theme = get_app().theme_manager.get_current_theme()
1151+
if theme:
1152+
theme.togglePlayIcon(False)
11531153

11541154
def actionSaveFrame_trigger(self, checked=True):
11551155
log.info("actionSaveFrame_trigger")
@@ -2992,19 +2992,22 @@ def foundCurrentVersion(self, version):
29922992
# Add toolbar button for non-cosmic dusk themes
29932993
# Cosmic dusk has a hidden toolbar button which is made visible
29942994
# by the setVisible() call above this
2995-
from themes.manager import ThemeManager, ThemeName
2996-
theme = ThemeManager().get_current_theme()
2997-
if theme and theme.name != ThemeName.COSMIC.value:
2998-
# Add spacer and 'New Version Available' toolbar button (default hidden)
2999-
spacer = QWidget(self)
3000-
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
3001-
self.toolBar.addWidget(spacer)
3002-
3003-
# Add update available button (with icon and text)
3004-
updateButton = QToolButton(self)
3005-
updateButton.setDefaultAction(self.actionUpdate)
3006-
updateButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
3007-
self.toolBar.addWidget(updateButton)
2995+
if get_app().theme_manager:
2996+
from themes.manager import ThemeName
2997+
theme = get_app().theme_manager.get_current_theme()
2998+
if theme and theme.name != ThemeName.COSMIC.value:
2999+
# Add spacer and 'New Version Available' toolbar button (default hidden)
3000+
spacer = QWidget(self)
3001+
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
3002+
self.toolBar.addWidget(spacer)
3003+
3004+
# Add update available button (with icon and text)
3005+
updateButton = QToolButton(self)
3006+
updateButton.setDefaultAction(self.actionUpdate)
3007+
updateButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
3008+
self.toolBar.addWidget(updateButton)
3009+
else:
3010+
log.warning("No ThemeManager loaded yet. Skip update available button.")
30083011

30093012
# Initialize sentry exception tracing (now that we know the current version)
30103013
from classes import sentry

src/windows/preferences.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -566,9 +566,8 @@ def dropdown_index_changed(self, widget, param, index):
566566

567567
if param["setting"] == "theme":
568568
# Apply selected theme to UI
569-
from themes.manager import ThemeManager, ThemeName
570-
theme_enum = ThemeName.find_by_name(value)
571-
ThemeManager().apply_theme(theme_enum)
569+
if get_app().theme_manager:
570+
get_app().theme_manager.apply_theme(value)
572571

573572
# Check for restart
574573
self.check_for_restart(param)

src/windows/video_widget.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
from classes.logger import log
4545
from classes.app import get_app
4646
from classes.query import Clip, Effect
47-
from themes.manager import ThemeManager
4847

4948

5049
class VideoWidget(QWidget, updates.UpdateInterface):
@@ -331,11 +330,15 @@ def paintEvent(self, event, *args):
331330
True)
332331

333332
# Get theme colors (if any)
334-
theme = ThemeManager().get_current_theme()
335-
if not theme:
336-
return
337-
background_color = theme.get_color(".video_widget", "background-color")
338-
painter.fillRect(event.rect(), background_color)
333+
if get_app().theme_manager:
334+
theme = get_app().theme_manager.get_current_theme()
335+
if not theme:
336+
log.warning("No theme loaded yet. Skip rendering video preview widget.")
337+
return
338+
background_color = theme.get_color(".video_widget", "background-color")
339+
painter.fillRect(event.rect(), background_color)
340+
else:
341+
log.warning("No ThemeManager loaded yet. Skip rendering video preview widget.")
339342

340343
# Find centered viewport
341344
viewport_rect = self.centeredViewport(self.width(), self.height())

src/windows/views/properties_tableview.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949

5050
from windows.models.properties_model import PropertiesModel
5151
from windows.color_picker import ColorPicker
52-
from themes.manager import ThemeManager
5352
from .menu import StyledContextMenu
5453

5554
import openshot
@@ -109,11 +108,15 @@ def paint(self, painter, option, index):
109108
value_percent = 0.0
110109

111110
# Get theme colors
112-
theme = ThemeManager().get_current_theme()
113-
if not theme:
114-
return
115-
foreground_color = theme.get_color(".property_value", "foreground-color")
116-
background_color = theme.get_color(".property_value", "background-color")
111+
if get_app().theme_manager:
112+
theme = get_app().theme_manager.get_current_theme()
113+
if not theme:
114+
log.warning("No theme loaded yet. Skip rendering properties widget.")
115+
return
116+
foreground_color = theme.get_color(".property_value", "foreground-color")
117+
background_color = theme.get_color(".property_value", "background-color")
118+
else:
119+
log.warning("No ThemeManager loaded yet. Skip rendering properties widget.")
117120

118121
# set background color
119122
painter.setPen(QPen(Qt.NoPen))

src/windows/views/zoom_slider.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from classes import updates
3939
from classes.app import get_app
4040
from classes.query import Clip, Track, Transition, Marker
41-
from themes.manager import ThemeManager
41+
from classes.logger import log
4242

4343

4444
class ZoomSlider(QWidget, updates.UpdateInterface):
@@ -108,10 +108,14 @@ def paintEvent(self, event, *args):
108108
event.accept()
109109

110110
# Get theme colors
111-
theme = ThemeManager().get_current_theme()
112-
if not theme:
113-
return
114-
playhead_color = theme.get_color(".zoom_slider_playhead", "background-color")
111+
if get_app().theme_manager:
112+
theme = get_app().theme_manager.get_current_theme()
113+
if not theme:
114+
log.warning("No theme loaded yet. Skip rendering zoom slider widget.")
115+
return
116+
playhead_color = theme.get_color(".zoom_slider_playhead", "background-color")
117+
else:
118+
log.warning("No ThemeManager loaded yet. Skip rendering zoom slider widget.")
115119

116120
# Paint timeline preview on QWidget
117121
painter = QPainter(self)

0 commit comments

Comments
 (0)