Skip to content

Commit 2099260

Browse files
authored
Major panes and pages (#1619)
* Plugin settings and loading * Fixes to database title * Fix pages failing to close
1 parent d5d99d0 commit 2099260

17 files changed

Lines changed: 277 additions & 252 deletions

File tree

activity_browser/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def run_activity_browser_no_launcher():
132132
modules = ModuleThread()
133133
modules.run()
134134

135-
from .ui.widgets import CentralTabWidget
135+
from .ui.widgets import ABCentralPagesWidget
136136
from .app import panes, pages, application, metadata
137137

138138
load_plugins()

activity_browser/app/actions/database/database_open.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def run(database_names: list[str]):
1919

2020
for db_name in database_names:
2121
db_pane = panes.DatabaseProductsPane(app.main_window, db_name)
22-
dock_widget = db_pane.getDockWidget(app.main_window)
22+
dock_widget = db_pane.getDockWidget()
2323
dock_widget.resize(dock_widget.width(), app.main_window.height() // 2)
2424

2525
app.main_window.addDockWidget(DatabaseOpen.get_area(), dock_widget)

activity_browser/app/main.py

Lines changed: 32 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -8,92 +8,60 @@
88
from activity_browser.ui import widgets
99

1010

11-
class MainWindow(QtWidgets.QMainWindow):
12-
_instance = None
13-
14-
def __new__(cls):
15-
if cls._instance is None:
16-
cls._instance = super().__new__(cls)
17-
cls._instance._initialized = False
18-
return cls._instance
19-
11+
class MainWindow(widgets.ABMainWindow):
2012

2113
def __init__(self, parent=None):
2214
from activity_browser.app.menu_bar import MenuBar
23-
24-
if self._initialized:
25-
return
26-
self._initialized = True
27-
2815
super().__init__(parent)
2916

30-
self.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
3117
self.setWindowTitle("Activity Browser")
32-
self.setDockNestingEnabled(True)
3318

3419
# Layout: extra items outside main layout
3520
self.menu_bar = MenuBar(self)
3621
self.setMenuBar(self.menu_bar)
3722

38-
self.central_widget = widgets.CentralTabWidget(self)
39-
self.central_widget.setTabsClosable(True)
23+
self.central_widget = widgets.ABCentralPagesWidget(self)
4024
self.setCentralWidget(self.central_widget)
41-
42-
# Initialize all base pages upfront (name -> widget instance)
43-
self.base_pages = {}
25+
4426
for page_name, page_class in app.pages.base_pages.items():
45-
page_instance = page_class()
46-
page_instance.setObjectName(page_name)
47-
self.base_pages[page_name] = page_instance
48-
49-
# Connect tab close signal
50-
self.central_widget.tabCloseRequested.connect(self._on_tab_close_requested)
27+
page_instance = page_class(parent=self.central_widget)
28+
self.central_widget.addPage(page_instance)
29+
self.menu_bar.view_menu.addAction(page_instance.toggleViewAction())
30+
31+
self.menu_bar.view_menu.addSeparator()
5132

5233
self.connect_signals()
53-
self.destroyed.connect(lambda: logger.warning("MainWindow destroyed"))
5434

55-
def event(self, event):
56-
if event.type() == QtCore.QEvent.Type.DeferredDelete:
57-
for page in self.base_pages.values():
58-
logger.debug(f"Destroying base page {page.__class__.__name__}: {id(page)}")
59-
try:
60-
page.deleteLater()
61-
except RuntimeError:
62-
# page already deleted
63-
pass
64-
return super().event(event)
35+
def connect_signals(self):
36+
app.signals.project.changed.connect(self.sync)
37+
app.signals.settings.changed.connect(self.apply_settings)
6538

6639
def sync(self):
6740
logger.log("SYNC", f"{self.__class__.__name__}: {id(self)}")
6841
self.sync_panes()
6942
self.sync_pages()
7043

7144
self.setWindowTitle(f"Activity Browser - {bd.projects.current}")
45+
self.central_widget.setCurrentIndex(0)
7246

7347
def sync_panes(self):
7448
self.clearPanes()
7549

76-
dws = []
77-
78-
# Iterate through the default panes and add them as dock widgets
50+
# Iterate through the base panes and add them
51+
panes = []
7952
for pane_name, pane_class in app.panes.base_panes.items():
8053
pane = pane_class(parent=self)
81-
dockwidget = pane.getDockWidget(self)
82-
dws.append(dockwidget)
54+
panes.append(pane)
55+
self.addPane(pane)
8356

84-
# Add the dock widget to the left dock area
85-
self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dockwidget)
86-
# Add the toggle view action to the menu bar
87-
self.menu_bar.view_menu.addAction(dockwidget.toggleViewAction())
57+
self.menu_bar.view_menu.addAction(pane.toggleViewAction())
8858

8959
# Hide the dock widget if it is marked as hidden
9060
if pane_name not in app.settings["startup"]["shown_panes"]:
91-
dockwidget.hide()
92-
93-
# Synchronize the pane
94-
pane.sync()
61+
pane.hide()
9562

9663
# Tabify the dock widgets for better organization
64+
dws = [pane.getDockWidget() for pane in panes]
9765
for dw in dws:
9866
if dw == dws[0]:
9967
continue
@@ -105,118 +73,23 @@ def sync_panes(self):
10573
def sync_pages(self):
10674
"""
10775
Synchronizes the central widget pages with the shown_pages setting.
108-
76+
10977
This method shows only those pages that are configured to be shown at startup.
11078
Pages are pre-initialized and just added/removed from tabs.
11179
"""
11280
# Get shown pages from settings
11381
shown_pages = app.settings["startup"].get("shown_pages", [])
114-
82+
11583
# Remove all pages from tabs first
116-
while self.central_widget.count() > 0:
117-
self.central_widget.removeTab(0)
118-
84+
for i in range(self.central_widget.count()):
85+
self.central_widget.closeTab(0)
86+
11987
# Add only the pages that should be shown
12088
for page_name in shown_pages:
121-
if page_name in self.base_pages:
122-
page_instance = self.base_pages[page_name]
123-
# Base pages should show minimize button instead of close
124-
self.central_widget.addTab(page_instance, page_name, show_minimize=True)
125-
126-
def show_page(self, page_name: str):
127-
"""
128-
Show a page by adding it to the tabs.
129-
130-
Args:
131-
page_name: The name of the page to show
132-
"""
133-
if page_name not in self.base_pages:
134-
return
135-
136-
page_widget = self.base_pages[page_name]
137-
138-
# Check if page is already in tabs
139-
index = self.central_widget.indexOf(page_widget)
140-
if index >= 0:
141-
# Already shown, just switch to it
142-
self.central_widget.setCurrentIndex(index)
143-
else:
144-
# Add to tabs with minimize button
145-
self.central_widget.addTab(page_widget, page_name, show_minimize=True)
146-
self.central_widget.setCurrentWidget(page_widget)
147-
148-
def hide_page(self, page_name: str):
149-
"""
150-
Hide a page by removing it from the tabs (but not destroying it).
151-
152-
Args:
153-
page_name: The name of the page to hide
154-
"""
155-
if page_name not in self.base_pages:
156-
return
157-
158-
page_widget = self.base_pages[page_name]
159-
index = self.central_widget.indexOf(page_widget)
160-
if index >= 0:
161-
self.central_widget.removeTab(index)
162-
163-
def toggle_page(self, page_name: str):
164-
"""
165-
Toggle a page shown/hidden.
166-
167-
Args:
168-
page_name: The name of the page to toggle
169-
"""
170-
if page_name not in self.base_pages:
171-
return
172-
173-
page_widget = self.base_pages[page_name]
174-
index = self.central_widget.indexOf(page_widget)
175-
176-
if index >= 0:
177-
# Page is shown, hide it
178-
self.hide_page(page_name)
179-
else:
180-
# Page is hidden, show it
181-
self.show_page(page_name)
182-
183-
def is_page_visible(self, page_name: str) -> bool:
184-
"""
185-
Check if a page is currently visible in the tabs.
186-
187-
Args:
188-
page_name: The name of the page to check
189-
190-
Returns:
191-
bool: True if the page is visible, False otherwise
192-
"""
193-
if page_name not in self.base_pages:
194-
return False
195-
196-
page_widget = self.base_pages[page_name]
197-
return self.central_widget.indexOf(page_widget) >= 0
198-
199-
def _on_tab_close_requested(self, index: int):
200-
"""
201-
Handle when user clicks the close button on a tab.
202-
For base pages, we just hide them instead of destroying them.
203-
204-
Args:
205-
index: The index of the tab to close
206-
"""
207-
widget = self.central_widget.widget(index)
208-
if widget is None:
209-
return
210-
211-
# Check if this is a base page
212-
page_name = widget.objectName()
213-
if page_name in self.base_pages:
214-
# Just remove from tabs, don't destroy
215-
self.central_widget.removeTab(index)
216-
else:
217-
# For non-base pages, remove and destroy
218-
self.central_widget.removeTab(index)
219-
widget.deleteLater()
89+
if page_name in app.pages.base_pages:
90+
page = self.findChild(app.pages.base_pages[page_name])
91+
if page:
92+
self.central_widget.addPage(page)
22093

22194
def apply_settings(self, load=False):
22295

@@ -243,35 +116,15 @@ def apply_settings(self, load=False):
243116
# apply pane tab position
244117
position = app.settings["appearance"]["pane_tab_position"]
245118
if position == "top":
246-
qt_position = QtWidgets.QTabWidget.North
119+
qt_position = QtWidgets.QTabWidget.TabPosition.North
247120
if position == "bottom":
248-
qt_position = QtWidgets.QTabWidget.South
121+
qt_position = QtWidgets.QTabWidget.TabPosition.South
249122
if position == "left":
250-
qt_position = QtWidgets.QTabWidget.West
123+
qt_position = QtWidgets.QTabWidget.TabPosition.West
251124
if position == "right":
252-
qt_position = QtWidgets.QTabWidget.East
125+
qt_position = QtWidgets.QTabWidget.TabPosition.East
253126
self.setTabPosition(QtCore.Qt.DockWidgetArea.AllDockWidgetAreas, qt_position)
254127

255-
def connect_signals(self):
256-
app.signals.project.changed.connect(self.sync)
257-
app.signals.settings.changed.connect(self.apply_settings)
258-
259-
def clearPanes(self):
260-
for pane in self.panes():
261-
logger.debug(f"Clearing pane {pane.__class__.__name__}: {id(pane)}")
262-
pane.deleteLater()
263-
264-
def panes(self):
265-
"""
266-
Return a list of all panes in the main window.
267-
"""
268-
from activity_browser.ui import widgets
269-
QtWidgets.QApplication.processEvents()
270-
return self.findChildren(widgets.ABAbstractPane)
271-
272-
def set_titlebar(self):
273-
self.setWindowTitle(f"Activity Browser - {bd.projects.current}")
274-
275128
def dialog_on_exception(self, exception: Exception):
276129
QtWidgets.QMessageBox.critical(
277130
self,

activity_browser/app/menu_bar.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -124,28 +124,28 @@ class ViewMenu(QtWidgets.QMenu):
124124
def __init__(self, parent=None) -> None:
125125
super().__init__(parent)
126126
self.setTitle("&View")
127-
128-
129-
# Populate pages
130-
self.page_actions = {}
131-
for page_name in app.pages.base_pages.keys():
132-
action = QtWidgets.QAction(page_name, self)
133-
action.setCheckable(True)
134-
action.triggered.connect(lambda checked, name=page_name: app.main_window.toggle_page(name))
135-
# Update checked state when menu is about to show
136-
self.page_actions[page_name] = action
137-
self.addAction(action)
138-
139-
# Update the checked state when menu is about to show
140-
self.aboutToShow.connect(self.update_page_actions)
141-
142-
self.addSeparator()
143-
144-
def update_page_actions(self):
145-
"""Update the checked state of page actions based on which pages are visible."""
146-
for page_name, action in self.page_actions.items():
147-
is_visible = app.main_window.is_page_visible(page_name)
148-
action.setChecked(is_visible)
127+
#
128+
#
129+
# # Populate pages
130+
# self.page_actions = {}
131+
# for page_name in app.pages.base_pages.keys():
132+
# action = QtWidgets.QAction(page_name, self)
133+
# action.setCheckable(True)
134+
# action.triggered.connect(lambda checked, name=page_name: app.main_window.toggle_page(name))
135+
# # Update checked state when menu is about to show
136+
# self.page_actions[page_name] = action
137+
# self.addAction(action)
138+
#
139+
# # # Update the checked state when menu is about to show
140+
# # self.aboutToShow.connect(self.update_page_actions)
141+
#
142+
# self.addSeparator()
143+
#
144+
# # def update_page_actions(self):
145+
# # """Update the checked state of page actions based on which pages are visible."""
146+
# # for page_name, action in self.page_actions.items():
147+
# # is_visible = app.main_window.is_page_visible(page_name)
148+
# # action.setChecked(is_visible)
149149

150150

151151
class CalculateMenu(QtWidgets.QMenu):

activity_browser/app/pages/activity_details/activity_details.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from .consumers_tab import ConsumersTab
1818

1919

20-
class ActivityDetailsPage(QtWidgets.QWidget):
20+
class ActivityDetailsPage(widgets.ABAbstractPage):
2121
"""
2222
A widget that displays detailed information about a specific activity.
2323

activity_browser/app/pages/calculation_setup/calculation_setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .impact_category_section import ImpactCategorySection
1010

1111

12-
class CalculationSetupPage(QtWidgets.QWidget):
12+
class CalculationSetupPage(widgets.ABAbstractPage):
1313

1414
def __init__(self, cs_name: str, parent=None):
1515
super().__init__(parent)

activity_browser/app/pages/impact_category_details/impact_category_details.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
from .impact_category_header import ImpactCategoryHeader
1313

1414

15-
class ImpactCategoryDetailsPage(QtWidgets.QWidget):
15+
class ImpactCategoryDetailsPage(widgets.ABAbstractPage):
1616
def __init__(self, name: tuple, parent=None):
1717
super().__init__(parent)
1818
self.name = name
1919
self.impact_category = bd.Method(name)
2020
self.is_editable = False
2121

22-
self.setObjectName(" | ".join(name))
22+
self.setObjectName("_".join(name))
23+
self.setWindowTitle(" | ".join(name))
2324

2425
self.header = ImpactCategoryHeader(self)
2526

activity_browser/app/pages/metadatastore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from activity_browser.app import metadata, signals
66

77

8-
class MetaDataStorePage(QtWidgets.QWidget):
8+
class MetaDataStorePage(widgets.ABAbstractPage):
99
def __init__(self, parent=None):
1010
super().__init__(parent)
1111
self.setObjectName("MetaDataStorePage")

0 commit comments

Comments
 (0)