Commit d3e2e823 authored by Samuel GAIST's avatar Samuel GAIST
Browse files

[widgets][assetwidget] Refactor AssetWidget

The AssetWidget class shows the correct editor for the JSON
data to be loaded.

It also handles the saving part.
parent 3ce7b883
......@@ -23,12 +23,83 @@
# #
###############################################################################
import simplejson as json
from PyQt5.QtCore import QFileSystemWatcher
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QTabWidget
from PyQt5.QtWidgets import QTextEdit
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QStackedWidget
from PyQt5.QtWidgets import QMessageBox
from ..backend.assetmodel import AssetType
from .editor import PlaceholderEditor
from .algorithmeditor import AlgorithmEditor
from .databaseeditor import DatabaseEditor
from .dataformateditor import DataformatEditor
from .experimenteditor import ExperimentEditor
from .libraryeditor import LibraryEditor
from .plottereditor import PlotterEditor
from .plotterparameterseditor import PlotterParametersEditor
from .toolchaineditor import ToolchainEditor
def widget_for_asset_type(asset_type):
"""Factory method to create the correct widget for the given asset_type.
:param asset_type AssetType: type of asset
:return: the editor matching the asset type
"""
if asset_type == AssetType.UNKNOWN:
return PlaceholderEditor()
if asset_type == AssetType.ALGORITHM:
return AlgorithmEditor()
elif asset_type == AssetType.DATABASE:
return DatabaseEditor()
elif asset_type == AssetType.DATAFORMAT:
return DataformatEditor()
elif asset_type == AssetType.EXPERIMENT:
return ExperimentEditor()
elif asset_type == AssetType.LIBRARY:
return LibraryEditor()
elif asset_type == AssetType.PLOTTER:
return PlotterEditor()
elif asset_type == AssetType.PLOTTERPARAMETERS:
return PlotterParametersEditor()
elif asset_type == AssetType.TOOLCHAIN:
return ToolchainEditor()
else:
raise RuntimeError("Invalid asset type given {}".format(asset_type))
class FileBlocker:
"""Context manager to suspend QFileSystemWatcher from watching a file"""
def __init__(self, watcher, path):
"""Constructor
:param watch QFileSystemWatcher: watcher to suspend
:param path str: path to suspend watching from
"""
self.watcher = watcher
self.path = path
def __enter__(self):
"""Stop watching file"""
self.watcher.removePath(self.path)
def __exit__(self, *args):
"""Start watching file again"""
self.watcher.addPath(self.path)
class AssetWidget(QWidget):
......@@ -44,30 +115,155 @@ class AssetWidget(QWidget):
super(AssetWidget, self).__init__(parent)
self.current_json = None
self.watcher = QFileSystemWatcher()
self.jsonWidget = QTextEdit()
self.jsonWidget.setReadOnly(True)
self.editor = QWidget()
self.editors = QStackedWidget()
self.editors_type = {}
for asset_type in AssetType:
editor = widget_for_asset_type(asset_type)
self.editors.addWidget(editor)
self.editors_type[asset_type] = editor
self.tabWidget = QTabWidget()
self.tabWidget.addTab(self.editor, self.tr("Editor"))
self.tabWidget.addTab(self.editors, self.tr("Editor"))
self.tabWidget.addTab(self.jsonWidget, self.tr("Raw JSON"))
layout = QVBoxLayout(self)
layout.addWidget(self.tabWidget)
self.watcher = QFileSystemWatcher()
self.save_button = QPushButton(self.tr("Save"))
self.save_button.setEnabled(False)
button_layout = QHBoxLayout()
button_layout.addStretch(1)
button_layout.addWidget(self.save_button)
layout.addLayout(button_layout)
self.watcher.fileChanged.connect(self.__reload_from_harddrive)
self.save_button.clicked.connect(self.save_json)
def __asset_type_for_path(self, path):
"""Returns the AssetType matching the path given
:param path str: path to asset
self.watcher.fileChanged.connect(self.show_json)
:return: AssetType matching the path given
"""
asset_path = path[len(self.prefix_root_path) :] # noqa
asset_str = asset_path.split("/")[1]
return AssetType.from_path(asset_str)
def __enable_save(self):
"""Enable the save button"""
self.save_button.setEnabled(True)
def maybe_save(self):
"""If the editor has been modified ask for saving"""
if self.current_editor.is_dirty():
answer = QMessageBox.question(
self,
self.tr("Save ?"),
self.tr("Content changed.\n" "Do you want to save ?"),
)
if answer == QMessageBox.Yes:
self.save_json()
else:
self.current_editor.clear_dirty()
self.save_button.setEnabled(False)
def __reload_from_harddrive(self):
"""Reload the content of this widget from the hard drive"""
answer = QMessageBox.question(
self,
self.tr("File changed"),
self.tr("The file has changed on disk.\n" "Do you want to reload it ?"),
)
if answer == QMessageBox.Yes:
self.__update_content()
def __update_content(self):
"""Update the content of this widget"""
try:
self.current_editor.dataChanged.disconnect(self)
except TypeError:
# Nothing was connected yet
pass
with open(self.current_json) as json_file:
json_data = json_file.read()
self.jsonWidget.setText(json_data)
asset_type = self.__asset_type_for_path(self.current_json)
editor = self.editors_type[asset_type]
editor.load_json(json.loads(json_data))
editor.dataChanged.connect(self.__enable_save)
self.editors.setCurrentWidget(editor)
self.save_button.setEnabled(False)
def closeEvent(self, event):
"""Re-impl will check and ask to save if the editor is dirty"""
def show_json(self, file_path):
""" Display the content of the file given in parameter
self.maybe_save()
super(AssetWidget, self).closeEvent(event)
@property
def current_editor(self):
"""Returns the current visible editor """
return self.editors.currentWidget()
def set_prefix_root(self, prefix_root_path):
"""Sets the root path of the prefix to edit"""
self.prefix_root_path = prefix_root_path
def load_json(self, file_path):
""" Load the content of the file given in parameter
:param file_path: path to the json file to load
"""
if self.current_json == file_path and not self.current_editor.is_dirty():
return
self.maybe_save()
self.current_json = file_path
files = self.watcher.files()
if files:
self.watcher.removePaths(files)
self.watcher.addPath(file_path)
self.watcher.addPath(self.current_json)
self.__update_content()
def save_json(self):
"""Save the editor content back to the file"""
json_data = self.current_editor.dump_json()
# Currently merging until we have all editors implemented
with open(self.current_json, "rt") as json_file:
original = json.loads(json_file.read())
original.update(json_data)
with FileBlocker(self.watcher, self.current_json):
with open(self.current_json, "wt") as json_file:
json_file.write(json.dumps(original, sort_keys=True, indent=4))
with open(file_path) as json_file:
self.jsonWidget.setText(json_file.read())
self.current_editor.clear_dirty()
self.save_button.setEnabled(False)
......@@ -27,6 +27,7 @@ from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import qApp
from PyQt5.QtCore import QSettings
from .assetbrowser import AssetBrowser
......@@ -61,7 +62,7 @@ class MainWindow(QMainWindow):
layout.addWidget(self.assetWidget)
self.setCentralWidget(centralWidget)
self.assetBrowser.json_selected.connect(self.assetWidget.show_json)
self.assetBrowser.json_selected.connect(self.assetWidget.load_json)
settingsAction.triggered.connect(self.show_settings)
self.load_settings()
......@@ -70,6 +71,7 @@ class MainWindow(QMainWindow):
"""Sets the root path of the prefix to edit"""
self.assetBrowser.set_prefix_root(prefix_root_path)
self.assetWidget.set_prefix_root(prefix_root_path)
def show_settings(self):
"""Show settings dialog"""
......@@ -89,5 +91,6 @@ class MainWindow(QMainWindow):
self.restoreState(settings.value("windowState"))
def closeEvent(self, event):
self.assetWidget.maybe_save()
self.save_settings()
QMainWindow.closeEvent(self, event)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment