Skip to content
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
51 changes: 51 additions & 0 deletions rascal2/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import logging
import multiprocessing
import re
from contextlib import suppress

from PyQt6 import QtWidgets

from rascal2.config import MatlabHelper, handle_scaling, setup_logging
from rascal2.paths import IMAGES_PATH, STATIC_PATH
from rascal2.ui.view import MainWindowView


def ui_execute(splash):
"""Create main window and executes GUI event loop.

Returns
-------
exit code : int
QApplication exit code
"""
handle_scaling()
QtWidgets.QApplication.setStyle("Fusion")
app = QtWidgets.QApplication.instance()
with suppress(FileNotFoundError), open(STATIC_PATH / "style.css") as stylesheet:
palette = app.palette()
replacements = {
"@Path": IMAGES_PATH.as_posix(),
"@Window": palette.window().color().name(),
"@Highlight": palette.highlight().color().name(),
"@Midlight": palette.midlight().color().name(),
"@Text": palette.text().color().name(),
}
style = re.sub("|".join(replacements), lambda x: replacements[x.group(0)], stylesheet.read())
app.setStyleSheet(style)

window = MainWindowView()
window.show()
splash.finish(window)

return app.exec()


def start_app(splash):
"""Start RasCAL app."""
multiprocessing.set_start_method("spawn", force=True)
setup_logging()
matlab_helper = MatlabHelper()
exit_code = ui_execute(splash)
matlab_helper.close_event.set()
logging.shutdown()
return exit_code
38 changes: 1 addition & 37 deletions rascal2/config.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
import logging
import os

os.environ["DELAY_MATLAB_START"] = "1"
import multiprocessing as mp
import pathlib
import platform
import site
import sys

from rascal2.paths import MATLAB_ARCH_FILE
from rascal2.settings import Settings, get_global_settings

if getattr(sys, "frozen", False):
# we are running in a bundle
SOURCE_PATH = pathlib.Path(sys.executable).parent.parent
SITE_PATH = SOURCE_PATH / "bin/_internal"
if pathlib.Path(SOURCE_PATH / "MacOS").is_dir():
SOURCE_PATH = SOURCE_PATH / "Resources"
SITE_PATH = SOURCE_PATH
EXAMPLES_PATH = SOURCE_PATH / "examples"
else:
SOURCE_PATH = pathlib.Path(__file__).parent
SITE_PATH = site.getsitepackages()[-1]
EXAMPLES_PATH = SOURCE_PATH.parent / "examples"

STATIC_PATH = SOURCE_PATH / "static"
IMAGES_PATH = STATIC_PATH / "images"
MATLAB_ARCH_FILE = pathlib.Path(SITE_PATH) / "matlab/engine/_arch.txt"
EXAMPLES_TEMP_PATH = pathlib.Path(get_global_settings().fileName()).parent / "examples"
LOGGER = logging.getLogger("rascal2")
SETTINGS = Settings()

Expand All @@ -39,22 +19,6 @@ def handle_scaling():
windll.user32.SetProcessDPIAware()


def path_for(filename: str):
"""Get full path for the given image file.

Parameters
----------
filename : str
basename and extension of image.

Returns
-------
full path : str
full path of the image.
"""
return (IMAGES_PATH / filename).as_posix()


def log_uncaught_exceptions(exc_type, exc_value, exc_traceback):
"""Qt slots swallows exceptions but this ensures exceptions are logged."""
logging.critical("An unhandled exception occurred!", exc_info=(exc_type, exc_value, exc_traceback))
Expand Down
4 changes: 2 additions & 2 deletions rascal2/core/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ def update_calculation_outputs(
self.set_parameter_values(problem)
self.presenter.model.update_results(copy.deepcopy(results))
self.presenter.model.result_log = log
chi_text = "" if results is None else f"{results.calculationResults.sumChi:.6g}"
self.presenter.view.controls_widget.chi_squared.setText(chi_text)
chi = "" if results is None else results.calculationResults.sumChi
self.presenter.view.controls_widget.update_chi_squared(chi)
self.presenter.view.terminal_widget.clear()
self.presenter.view.terminal_widget.write(log)
self.presenter.view.project_widget.update_project_view()
3 changes: 2 additions & 1 deletion rascal2/dialogs/about_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

import rascal2
import rascal2.widgets
from rascal2.config import MatlabHelper, path_for
from rascal2.config import MatlabHelper
from rascal2.paths import path_for
from rascal2.settings import get_global_settings


Expand Down
3 changes: 2 additions & 1 deletion rascal2/dialogs/custom_file_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from PyQt6 import Qsci, QtGui, QtWidgets
from ratapi.utils.enums import Languages

from rascal2.config import EXAMPLES_PATH, LOGGER, SETTINGS, MatlabHelper
from rascal2.config import LOGGER, SETTINGS, MatlabHelper
from rascal2.core.enums import CustomFileType
from rascal2.paths import EXAMPLES_PATH

MATLAB_MODEL_TEMPLATE = """function [output, sub_rough] = {0}{1}
% RasCAL-2 Layer Model Custom File.
Expand Down
3 changes: 2 additions & 1 deletion rascal2/dialogs/settings_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from PyQt6 import QtCore, QtWidgets

from rascal2.config import LOGGER, MATLAB_ARCH_FILE, SETTINGS, MatlabHelper
from rascal2.config import LOGGER, SETTINGS, MatlabHelper
from rascal2.paths import MATLAB_ARCH_FILE
from rascal2.settings import SettingsGroups
from rascal2.widgets.inputs import get_validated_input

Expand Down
3 changes: 2 additions & 1 deletion rascal2/dialogs/startup_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

from PyQt6 import QtCore, QtWidgets

from rascal2.config import EXAMPLES_PATH, LOGGER
from rascal2.config import LOGGER
from rascal2.core.worker import Worker
from rascal2.paths import EXAMPLES_PATH
from rascal2.settings import update_recent_projects

# global variable for required project files
Expand Down
85 changes: 41 additions & 44 deletions rascal2/main.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,51 @@
import logging
import multiprocessing
import re
import os
import sys
from contextlib import suppress

from PyQt6 import QtGui, QtWidgets

from rascal2.config import IMAGES_PATH, STATIC_PATH, MatlabHelper, handle_scaling, path_for, setup_logging
from rascal2.ui.view import MainWindowView


def ui_execute():
"""Create main window and executes GUI event loop.

Returns
-------
exit code : int
QApplication exit code
"""
handle_scaling()
QtWidgets.QApplication.setStyle("Fusion")
app = QtWidgets.QApplication(sys.argv[:1])
app.setWindowIcon(QtGui.QIcon(path_for("logo.png")))
with suppress(FileNotFoundError), open(STATIC_PATH / "style.css") as stylesheet:
palette = app.palette()
replacements = {
"@Path": IMAGES_PATH.as_posix(),
"@Window": palette.window().color().name(),
"@Highlight": palette.highlight().color().name(),
"@Midlight": palette.midlight().color().name(),
"@Text": palette.text().color().name(),
}
style = re.sub("|".join(replacements), lambda x: replacements[x.group(0)], stylesheet.read())
app.setStyleSheet(style)

window = MainWindowView()
window.show()
return app.exec()

from PyQt6.QtCore import Qt, QThread
from PyQt6.QtGui import QIcon, QPixmap
from PyQt6.QtWidgets import QApplication, QSplashScreen

from rascal2.paths import path_for


class SplashScreen(QSplashScreen):
"""Create splash screen widget."""

def __init__(self, *args):
super().__init__(*args)
self.painted = False

def paintEvent(self, event):
super().paintEvent(event)
self.painted = True


def main():
"""Entry point function for starting RasCAL."""
multiprocessing.freeze_support()
multiprocessing.set_start_method("spawn", force=True)
setup_logging()
matlab_helper = MatlabHelper()
exit_code = ui_execute()
matlab_helper.close_event.set()
logging.shutdown()
sys.exit(exit_code)

app = QApplication([])
app.setWindowIcon(QIcon(path_for("logo.png")))

splash = SplashScreen(QPixmap(path_for("splash.png")), Qt.WindowType.WindowStaysOnTopHint)
splash.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
splash.show()
splash.raise_()
splash.activateWindow()
app.processEvents()
for _ in range(100):
if splash.painted:
break
# wait for splash to paint on linux
QThread.usleep(100)
app.processEvents()
app.processEvents()

os.environ["DELAY_MATLAB_START"] = "1"
from rascal2.app import start_app

sys.exit(start_app(splash))


if __name__ == "__main__":
Expand Down
39 changes: 39 additions & 0 deletions rascal2/paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import site
import sys
from pathlib import Path

from rascal2.settings import get_global_settings

if getattr(sys, "frozen", False):
# we are running in a bundle
SOURCE_PATH = Path(sys.executable).parent.parent
SITE_PATH = SOURCE_PATH / "bin/_internal"
if Path(SOURCE_PATH / "MacOS").is_dir():
SOURCE_PATH = SOURCE_PATH / "Resources"
SITE_PATH = SOURCE_PATH
EXAMPLES_PATH = SOURCE_PATH / "examples"
else:
SOURCE_PATH = Path(__file__).parent
SITE_PATH = site.getsitepackages()[-1]
EXAMPLES_PATH = SOURCE_PATH.parent / "examples"

STATIC_PATH = SOURCE_PATH / "static"
IMAGES_PATH = STATIC_PATH / "images"
MATLAB_ARCH_FILE = Path(SITE_PATH) / "matlab/engine/_arch.txt"
EXAMPLES_TEMP_PATH = Path(get_global_settings().fileName()).parent / "examples"


def path_for(filename: str):
"""Get full path for the given image file.

Parameters
----------
filename : str
basename and extension of image.

Returns
-------
full path : str
full path of the image.
"""
return (IMAGES_PATH / filename).as_posix()
Binary file modified rascal2/static/images/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added rascal2/static/images/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion rascal2/ui/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import ratapi.outputs
from PyQt6 import QtCore

from rascal2.config import EXAMPLES_PATH, EXAMPLES_TEMP_PATH
from rascal2.paths import EXAMPLES_PATH, EXAMPLES_TEMP_PATH


def copy_example_project(load_path):
Expand Down
2 changes: 1 addition & 1 deletion rascal2/ui/presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def handle_event(self):
self.view.terminal_widget.write(event)
chi_squared = get_live_chi_squared(event, str(self.model.controls.procedure))
if chi_squared is not None:
self.view.controls_widget.chi_squared.setText(chi_squared)
self.view.controls_widget.update_chi_squared(chi_squared)
case rat.events.ProgressEventData():
self.view.terminal_widget.update_progress(event)
case rat.events.PlotEventData():
Expand Down
5 changes: 3 additions & 2 deletions rascal2/ui/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

from PyQt6 import QtCore, QtGui, QtWidgets

from rascal2.config import EXAMPLES_PATH, EXAMPLES_TEMP_PATH, SETTINGS, path_for
from rascal2.config import SETTINGS
from rascal2.core.enums import UnsavedReply
from rascal2.dialogs.about_dialog import AboutDialog
from rascal2.dialogs.settings_dialog import SettingsDialog
from rascal2.dialogs.startup_dialog import PROJECT_FILES, LoadDialog, LoadR1Dialog, NewProjectDialog, StartupDialog
from rascal2.paths import EXAMPLES_PATH, EXAMPLES_TEMP_PATH, path_for
from rascal2.settings import MDIGeometries, get_global_settings
from rascal2.widgets import ControlsWidget, PlotWidget, TerminalWidget
from rascal2.widgets.project import ProjectWidget
Expand Down Expand Up @@ -403,7 +404,7 @@ def handle_results(self, results=None):
"""
self.controls_widget.run_button.setChecked(False)
if results is not None:
self.controls_widget.chi_squared.setText(f"{results.calculationResults.sumChi:.6g}")
self.controls_widget.update_chi_squared(results.calculationResults.sumChi)

def set_editing_enabled(self, enabled: bool):
"""Disable or enable project editing, for example during a run.
Expand Down
14 changes: 14 additions & 0 deletions rascal2/widgets/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def __init__(self, parent):
chi_layout = QtWidgets.QHBoxLayout()
self.chi_squared = QtWidgets.QLineEdit()
self.chi_squared.setReadOnly(True)
self.chi_squared.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
chi_layout.addWidget(QtWidgets.QLabel("Current chi-squared:"))
chi_layout.addWidget(self.chi_squared)
chi_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignVCenter)
Expand Down Expand Up @@ -169,6 +170,19 @@ def set_procedure(self, index: int):
if field not in ["procedure"]:
self.fit_settings_layout.currentWidget().update_data(field)

def update_chi_squared(self, value):
"""Update chi squared to given value.

Parameters
----------
value : str or float
chi squared value.

"""
chi = value if isinstance(value, str) else f"{value:.6g}"
self.chi_squared.setText(chi)
self.chi_squared.home(False)


class FitSettingsWidget(QtWidgets.QWidget):
"""Widget containing the fit settings form.
Expand Down
2 changes: 1 addition & 1 deletion rascal2/widgets/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pydantic.fields import FieldInfo
from PyQt6 import QtCore, QtGui, QtWidgets

from rascal2.config import path_for
from rascal2.paths import path_for


def get_validated_input(field_info: FieldInfo, parent=None) -> QtWidgets.QWidget:
Expand Down
Loading
Loading