Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indicate job start and completion #604

Merged
merged 5 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions MDANSE_GUI/Src/MDANSE_GUI/TabbedWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
QMainWindow,
QFileDialog,
QToolBar,
QTabWidget,
QMenuBar,
QMessageBox,
QApplication,
Expand All @@ -48,6 +47,7 @@
from MDANSE_GUI.Tabs.PlotTab import PlotTab
from MDANSE_GUI.Tabs.InstrumentTab import InstrumentTab
from MDANSE_GUI.Widgets.StyleDialog import StyleDialog, StyleDatabase
from MDANSE_GUI.Widgets.NotificationTabWidget import NotificationTabWidget


class TabbedWindow(QMainWindow):
Expand All @@ -71,7 +71,7 @@ def __init__(
**kwargs,
):
super().__init__(parent, *args, **kwargs)
self.tabs = QTabWidget(self)
self.tabs = NotificationTabWidget(self)
self.setCentralWidget(self.tabs)
self._views = defaultdict(list)
self._actions = []
Expand Down Expand Up @@ -115,6 +115,7 @@ def __init__(
self._tabs["Instruments"]._visualiser.instrument_details_changed.connect(
self._tabs["Actions"].update_action_after_instrument_change
)
self.tabs.currentChanged.connect(self.tabs.reset_current_color)

def createCommonModels(self):
self._trajectory_model = GeneralModel()
Expand Down Expand Up @@ -269,6 +270,7 @@ def createTrajectoryViewer(self):
self.tabs.addTab(trajectory_tab._core, name)
self._tabs[name] = trajectory_tab
self._job_holder.trajectory_for_loading.connect(trajectory_tab.load_trajectory)
self._job_holder.trajectory_for_loading.connect(trajectory_tab.tab_notification)

def createInstrumentSelector(self):
name = "Instruments"
Expand Down Expand Up @@ -335,6 +337,7 @@ def createPlotSelection(self):
self.tabs.addTab(plot_tab._core, name)
self._tabs[name] = plot_tab
self._job_holder.results_for_loading.connect(plot_tab.load_results)
self._job_holder.results_for_loading.connect(plot_tab.tab_notification)

def createPlotHolder(self):
name = "Plot Holder"
Expand Down
12 changes: 11 additions & 1 deletion MDANSE_GUI/Src/MDANSE_GUI/Tabs/GeneralTab.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from typing import Dict, Tuple

from qtpy.QtCore import QObject, Slot, QMessageLogger
from qtpy.QtCore import QObject, Slot, Signal, QMessageLogger
from qtpy.QtWidgets import QListView

from MDANSE.MLogging import LOG
Expand All @@ -41,7 +41,10 @@ class GeneralTab(QObject):
for accessing them from the outside.
"""

notify_user = Signal(int)

def __init__(self, *args, **kwargs):
self._my_tab_id = -1
self._name = kwargs.pop("name", "Unnamed GUI part")
self._session = kwargs.pop("session", LocalSession())
_ = kwargs.pop("settings", None)
Expand Down Expand Up @@ -69,6 +72,13 @@ def __init__(self, *args, **kwargs):
self._core.set_label_text(label_text)
self.propagate_session()

def set_my_id(self, tab_id: int):
self._my_tab_id = tab_id

@Slot()
def tab_notification(self):
self.notify_user.emit(self._my_tab_id)

def grouped_settings(self):
"""This method tells the Session object what settings
this Tab will store in its settings file,
Expand Down
2 changes: 2 additions & 0 deletions MDANSE_GUI/Src/MDANSE_GUI/Tabs/Models/JobHolder.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ class JobHolder(QStandardItemModel):

trajectory_for_loading = Signal(str)
results_for_loading = Signal(str)
new_job_started = Signal()

def __init__(self, parent: QObject = None):
super().__init__(parent=parent)
Expand Down Expand Up @@ -330,6 +331,7 @@ def startProcess(self, job_vars: list, load_afterwards=False):
item_th._stat_item,
]
)
self.new_job_started.emit()
# nrows = self.rowCount()
# index = self.indexFromItem(item_th._item)
# print(f"Index: {index}")
Expand Down
3 changes: 3 additions & 0 deletions MDANSE_GUI/Src/MDANSE_GUI/Tabs/PlotTab.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def gui_instance(
label_text=label_text,
)
the_tab._visualiser._unit_lookup = the_tab
the_tab._visualiser.new_entry.connect(the_tab.tab_notification)
return the_tab

@property
Expand All @@ -113,6 +114,8 @@ def accept_external_data(self, data_model):
self._visualiser.plotter.plot_data()
except Exception as e2:
LOG.error(f"Visualiser failed to plot data: {e2}")
else:
self.tab_notification()

@Slot(int)
def switch_model(self, tab_id):
Expand Down
1 change: 1 addition & 0 deletions MDANSE_GUI/Src/MDANSE_GUI/Tabs/RunTab.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def gui_instance(
),
label_text=run_tab_label,
)
the_tab._model.new_job_started.connect(the_tab.tab_notification)
return the_tab


Expand Down
4 changes: 3 additions & 1 deletion MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/Action.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from MDANSE.MLogging import LOG
from MDANSE.Framework.Jobs.IJob import IJob

from MDANSE_GUI.Widgets.DelayedButton import DelayedButton
from MDANSE_GUI.InputWidgets import *
from MDANSE_GUI.Tabs.Visualisers.InstrumentInfo import SimpleInstrument

Expand Down Expand Up @@ -245,7 +246,7 @@ def update_panel(self, job_name: str) -> None:
buttonlayout = QHBoxLayout(buttonbase)
buttonbase.setLayout(buttonlayout)
self.save_button = QPushButton("Save as script", buttonbase)
self.execute_button = QPushButton("RUN!", buttonbase)
self.execute_button = DelayedButton("RUN!", buttonbase, delay=3000)
self.execute_button.setStyleSheet("font-weight: bold")
self.post_execute_checkbox = QCheckBox("Auto-load results", buttonbase)
try:
Expand All @@ -260,6 +261,7 @@ def update_panel(self, job_name: str) -> None:

self.save_button.clicked.connect(self.save_dialog)
self.execute_button.clicked.connect(self.execute_converter)
self.execute_button.needs_updating.connect(self.allow_execution)

buttonlayout.addWidget(self.save_button)
buttonlayout.addWidget(self.execute_button)
Expand Down
3 changes: 3 additions & 0 deletions MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotHolder.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class PlotHolder(QTabWidget):
"""

error = Signal(str)
new_entry = Signal()

def __init__(self, *args, unit_lookup=None, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -67,6 +68,7 @@ def new_plot(self, tab_name: str) -> int:
self._context.append(plotting_context)
self._plotter.append(plotter)
self.setCurrentIndex(tab_id)
self.new_entry.emit()
return tab_id

@Slot(str)
Expand All @@ -82,6 +84,7 @@ def new_text(self, ignored_name: str) -> int:
self._context.append(plotting_context)
self._plotter.append(plotter)
self.setCurrentIndex(tab_id)
self.new_entry.emit()
return tab_id

@Slot(int)
Expand Down
51 changes: 51 additions & 0 deletions MDANSE_GUI/Src/MDANSE_GUI/Widgets/DelayedButton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This file is part of MDANSE_GUI.
#
# MDANSE_GUI is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.


from qtpy.QtCore import Slot, Signal, QObject, QTimer
from qtpy.QtWidgets import QPushButton


class DelayedButton(QPushButton):

needs_updating = Signal()

def __init__(self, *args, **kwargs):
delay_time = kwargs.pop("delay", 1000)
inactive_message = kwargs.pop("inactive_text", "Starting...")
super().__init__(*args, **kwargs)

self._delay_time = delay_time
self._inactive_message = inactive_message
self._freeze_updates = False
self._active_message = self.text()
self.clicked.connect(self.start_delay)

def setEnabled(self, a0: bool) -> None:
if not self._freeze_updates:
return super().setEnabled(a0)

@Slot()
def start_delay(self):
self.setText(self._inactive_message)
self.setEnabled(False)
self._freeze_updates = True
QTimer.singleShot(self._delay_time, self.end_delay)

@Slot()
def end_delay(self):
self.setText(self._active_message)
self._freeze_updates = False
self.needs_updating.emit()
52 changes: 52 additions & 0 deletions MDANSE_GUI/Src/MDANSE_GUI/Widgets/NotificationTabWidget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This file is part of MDANSE_GUI.
#
# MDANSE_GUI is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.


from qtpy.QtCore import Slot, Signal, QObject, QTimer
from qtpy.QtWidgets import QTabWidget
from qtpy.QtGui import QColor, QIcon


class NotificationTabWidget(QTabWidget):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._normal_colours = {}
self._special_color = QColor(250, 10, 50)

def addTab(self, widget: "QObject", name: str) -> int:
object_id = super().addTab(widget, name)
self._normal_colours[object_id] = self.tabBar().tabTextColor(object_id)
widget._tab_reference.set_my_id(object_id)
widget._tab_reference.notify_user.connect(self.set_special_color)
return object_id

@Slot(int)
def set_special_color(self, tab_index: int):
self.tabBar().setTabTextColor(tab_index, self._special_color)
self.tabBar().setTabIcon(
tab_index, QIcon.fromTheme(QIcon.ThemeIcon.DialogInformation)
)

@Slot(int)
def reset_current_color(self):
tab_index = self.tabBar().currentIndex()
self.tabBar().setTabTextColor(tab_index, self._normal_colours[tab_index])
self.tabBar().setTabIcon(tab_index, QIcon())

@Slot(int)
def reset_color(self, tab_index: int):
self.tabBar().setTabTextColor(tab_index, self._normal_colours[tab_index])
self.tabBar().setTabIcon(tab_index, QIcon())