Skip to content

Commit

Permalink
can now open matlab file in matlab
Browse files Browse the repository at this point in the history
  • Loading branch information
alexhroom committed Nov 15, 2024
1 parent 7da49f4 commit f91e9f0
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 12 deletions.
3 changes: 2 additions & 1 deletion rascal2/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from rascal2.core.runner import RATRunner
from rascal2.core.settings import Settings, get_global_settings
from rascal2.core.matlab import MatlabHandler

__all__ = ["RATRunner", "get_global_settings", "Settings"]
__all__ = ["MatlabHandler", "RATRunner", "get_global_settings", "Settings"]
40 changes: 40 additions & 0 deletions rascal2/core/matlab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""MATLAB engine that runs in the background."""

from contextlib import suppress


class MatlabHandler:
"""A singleton class that provides a MATLAB engine."""

_instance = None

def __init__(self):
raise RuntimeError("MatlabHandler should not be invoked directly. " "Use MatlabHandler.instance().")

@classmethod
def instance(cls):
"""Instantiate a MatlabHandler if one does not exist, or return the existing one if one exists."""
if cls._instance is None:
cls._instance = cls.__new__(cls)
cls._instance.init()
return cls._instance

def init(self):
"""Instantiate the MatlabHandler."""
self.future = None
self.engine = None
with suppress(ImportError):
import matlab.engine

self.future = matlab.engine.start_matlab(background=True)

def get_engine(self):
"""Get the MATLAB engine."""
if self.engine is not None:
return self.engine

if self.future is None:
raise ImportError("Attempted to start MATLAB engine, but `matlabengine` is not installed!")

self.engine = self.future.result()
return self.engine
34 changes: 28 additions & 6 deletions rascal2/dialogs/custom_file_editor.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"""Dialogs for editing custom files."""

import logging
from pathlib import Path

from PyQt6 import Qsci, QtWidgets
from RATapi.utils.enums import Languages

from rascal2.core import MatlabHandler


def edit_file(filename: str, language: Languages, parent: QtWidgets.QWidget):
"""Edit a file in the file editor.
Expand All @@ -19,31 +22,50 @@ def edit_file(filename: str, language: Languages, parent: QtWidgets.QWidget):
The parent of this widget.
"""
dialog = CustomFileEditorDialog(filename, language, parent)
file = Path(filename)
if not file.is_file():
logger = logging.getLogger("rascal_log")
logger.error("Attempted to edit a custom file which does not exist!")
return

dialog = CustomFileEditorDialog(file, language, parent)
dialog.exec()


def edit_file_matlab(filename: str):
"""Open a file in MATLAB."""
handler = MatlabHandler.instance()
try:
engine = handler.get_engine()
except ImportError as err:
logger = logging.getLogger("rascal_log")
logger.error(err)
return

engine.edit(filename)


class CustomFileEditorDialog(QtWidgets.QDialog):
"""Dialog for editing custom files.
Parameters
----------
filename : str
The name of the file to edit.
file : pathlib.Path
The file to edit.
language : Languages
The language for dialog highlighting.
parent : QtWidgets.QWidget
The parent of this widget.
"""

def __init__(self, filename, language, parent):
def __init__(self, file, language, parent):
super().__init__(parent)

self.setMinimumWidth(600)
self.setMinimumHeight(400)

self.file = Path(filename)
self.file = file

self.editor = Qsci.QsciScintilla()
match language:
Expand All @@ -70,7 +92,7 @@ def __init__(self, filename, language, parent):
layout.addLayout(button_layout)

self.setLayout(layout)
self.setWindowTitle(f"Edit {filename}")
self.setWindowTitle(f"Edit {str(file)}")

def save_file(self):
"""Save and close the file."""
Expand Down
42 changes: 37 additions & 5 deletions rascal2/widgets/project/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import rascal2.widgets.delegates as delegates
from rascal2.config import path_for
from rascal2.dialogs.custom_file_editor import edit_file
from rascal2.dialogs.custom_file_editor import edit_file, edit_file_matlab


class ClassListModel(QtCore.QAbstractTableModel):
Expand Down Expand Up @@ -457,7 +457,6 @@ def setData(self, index, value, role=QtCore.Qt.ItemDataRole.DisplayRole):
row = index.row()
self.classlist[row].path = file_path.parent
self.classlist[row].filename = str(file_path.name)
self.dataChanged.emit(index, index)

# auto-set language from file extension if possible
# & get file names for dropdown on Python
Expand Down Expand Up @@ -485,6 +484,7 @@ def setData(self, index, value, role=QtCore.Qt.ItemDataRole.DisplayRole):
if language is not None:
self.classlist[row].language = language

self.dataChanged.emit(index, index)
return True

return super().setData(index, value, role)
Expand Down Expand Up @@ -520,11 +520,43 @@ def edit(self):

def make_edit_button(self, index):
button = QtWidgets.QPushButton("Edit File", self.table)
q_scintilla_action = QtGui.QAction("Edit in RasCAL-2...", self.table)
q_scintilla_action.triggered.connect(
lambda: edit_file(
self.model.classlist[index].path / self.model.classlist[index].filename,
self.model.classlist[index].language,
self,
)
)
matlab_action = QtGui.QAction("Edit in MATLAB...", self.table)
matlab_action.triggered.connect(
lambda: edit_file_matlab(self.model.classlist[index].path / self.model.classlist[index].filename)
)
menu = QtWidgets.QMenu(self.table)
menu.addActions([q_scintilla_action, matlab_action])

def file_editable():
return (
self.model.data(self.model.index(index, self.model.headers.index("language") + 1)) != Languages.Cpp
) and (self.model.data(self.model.index(index, self.model.headers.index("filename") + 1)) != "Browse...")
language = self.model.data(self.model.index(index, self.model.headers.index("language") + 1))
with contextlib.suppress(TypeError):
button.pressed.disconnect()
match language:
case Languages.Matlab:
button.setMenu(menu)
button.pressed.connect(button.showMenu)
case Languages.Python:
button.setMenu(None)
button.pressed.connect(
lambda: edit_file(
self.model.classlist[index].path / self.model.classlist[index].filename,
Languages.Python,
self,
)
)

editable = (language != Languages.Cpp) and (
self.model.data(self.model.index(index, self.model.headers.index("filename") + 1)) != "Browse..."
)
return editable

button.setEnabled(file_editable())
self.model.dataChanged.connect(lambda: button.setEnabled(file_editable()))
Expand Down

0 comments on commit f91e9f0

Please sign in to comment.