[app] re-organization of ui classes
This commit is contained in:
parent
93d552265f
commit
20c1d02073
@ -1,12 +1,8 @@
|
|||||||
import os
|
from PyQt5.QtWidgets import QStackedLayout, QWidget
|
||||||
from PyQt5.QtWidgets import QComboBox, QFrame, QGroupBox, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QSplitter, QStackedLayout, QAbstractItemView, QTextEdit, QVBoxLayout, QWidget, QGridLayout
|
|
||||||
from PyQt5.QtGui import QPixmap
|
|
||||||
from PyQt5.QtCore import QItemSelectionModel, Qt, QSettings, pyqtSignal
|
|
||||||
|
|
||||||
from nixview.file_handler import FileHandler, ItemDescriptor
|
from nixview.ui.plotscreen import PlotScreen
|
||||||
import nixview.communicator as comm
|
from nixview.ui.splashscreen import SplashScreen
|
||||||
import nixview.constants as cnst
|
from nixview.ui.filescreen import FileScreen
|
||||||
from nixview.data_models.tree_model import NixTreeView, TreeModel, TreeType
|
|
||||||
|
|
||||||
class CentralWidget(QWidget):
|
class CentralWidget(QWidget):
|
||||||
|
|
||||||
@ -14,7 +10,7 @@ class CentralWidget(QWidget):
|
|||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
self._splash = SplashScreen()
|
self._splash = SplashScreen()
|
||||||
self._file_view = FileView(self)
|
self._file_view = FileScreen(self)
|
||||||
self._plot_screen = PlotScreen(self)
|
self._plot_screen = PlotScreen(self)
|
||||||
self._plot_screen.close_signal.connect(self.on_plot_close)
|
self._plot_screen.close_signal.connect(self.on_plot_close)
|
||||||
self._stack = QStackedLayout(self)
|
self._stack = QStackedLayout(self)
|
||||||
@ -38,221 +34,3 @@ class CentralWidget(QWidget):
|
|||||||
self._file_view.reset()
|
self._file_view.reset()
|
||||||
self._splash.reset()
|
self._splash.reset()
|
||||||
self._stack.setCurrentIndex(0)
|
self._stack.setCurrentIndex(0)
|
||||||
|
|
||||||
|
|
||||||
class FileView(QWidget):
|
|
||||||
def __init__(self, parent=None) -> None:
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self._file_handler = FileHandler()
|
|
||||||
|
|
||||||
vbox = QVBoxLayout()
|
|
||||||
self.setLayout(vbox)
|
|
||||||
|
|
||||||
main_splitter = QSplitter(Qt.Vertical)
|
|
||||||
self.layout().addWidget(main_splitter)
|
|
||||||
|
|
||||||
self._info = EntityInfo(self)
|
|
||||||
main_splitter.addWidget(self._info)
|
|
||||||
|
|
||||||
self._data_tree = NixTreeView(self)
|
|
||||||
|
|
||||||
self._tree_type_combo = QComboBox()
|
|
||||||
self._tree_type_combo.adjustSize()
|
|
||||||
self._tree_type_combo.addItems([TreeType.Data.value, TreeType.Full.value, TreeType.Metadata.value])
|
|
||||||
self._tree_type_combo.currentTextChanged.connect(self.update)
|
|
||||||
|
|
||||||
hbox = QHBoxLayout()
|
|
||||||
hbox.addWidget(QLabel("Tree type:"))
|
|
||||||
hbox.addWidget(self._tree_type_combo)
|
|
||||||
hbox.addStretch()
|
|
||||||
data_group = QGroupBox("Data")
|
|
||||||
data_vbox = QVBoxLayout()
|
|
||||||
data_vbox.setContentsMargins(1, 10, 1, 1)
|
|
||||||
|
|
||||||
data_vbox.addLayout(hbox)
|
|
||||||
data_vbox.addWidget(self._data_tree)
|
|
||||||
data_group.setLayout(data_vbox)
|
|
||||||
|
|
||||||
main_splitter.addWidget(data_group)
|
|
||||||
main_splitter.setSizes([200, 600])
|
|
||||||
vbox.addWidget(main_splitter)
|
|
||||||
|
|
||||||
def dataTreeSelection(self, current_index, last_index):
|
|
||||||
if not current_index.isValid():
|
|
||||||
return
|
|
||||||
item = current_index.internalPointer()
|
|
||||||
comm.communicator.item_selected.emit(item)
|
|
||||||
self._info.setEntityInfo(item.node_descriptor)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
tt = TreeType.Data
|
|
||||||
if self._tree_type_combo.currentText() == TreeType.Data.value:
|
|
||||||
tt = TreeType.Data
|
|
||||||
elif self._tree_type_combo.currentText() == TreeType.Full.value:
|
|
||||||
tt = TreeType.Full
|
|
||||||
elif self._tree_type_combo.currentText() == TreeType.Metadata.value:
|
|
||||||
tt = TreeType.Metadata
|
|
||||||
self._info.setEntityInfo(None)
|
|
||||||
data_model = TreeModel(self._file_handler, tt)
|
|
||||||
self._data_tree.setModel(data_model)
|
|
||||||
selection_model = QItemSelectionModel(data_model)
|
|
||||||
self._data_tree.setSelectionModel(selection_model)
|
|
||||||
selection_model.currentChanged.connect(self.dataTreeSelection)
|
|
||||||
for i in range(data_model.columnCount(None)):
|
|
||||||
self._data_tree.resizeColumnToContents(i)
|
|
||||||
self._info.setFileInfo(self._file_handler.file_descriptor)
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class EntityInfo(QWidget):
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self._file_handler = FileHandler()
|
|
||||||
self.setLayout(QHBoxLayout())
|
|
||||||
|
|
||||||
self._metadata_tree = NixTreeView()
|
|
||||||
|
|
||||||
mdata_grp = QGroupBox("Metadata")
|
|
||||||
mdata_grp.setLayout(QVBoxLayout())
|
|
||||||
mdata_grp.layout().setContentsMargins(1, 10, 1, 1)
|
|
||||||
mdata_grp.layout().addWidget(self._metadata_tree)
|
|
||||||
|
|
||||||
file_info_grp = QGroupBox("File info")
|
|
||||||
file_info_grp.setLayout(QVBoxLayout())
|
|
||||||
file_info_grp.layout().setContentsMargins(1, 10, 1, 1)
|
|
||||||
self._file_info = QTextEdit("File information")
|
|
||||||
self._file_info.setEnabled(True)
|
|
||||||
self._file_info.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse)
|
|
||||||
self._file_info.setFrameShape(QFrame.NoFrame)
|
|
||||||
self._file_info.setLineWrapMode(QTextEdit.WidgetWidth)
|
|
||||||
file_info_grp.layout().addWidget(self._file_info)
|
|
||||||
|
|
||||||
entity_info_grp = QGroupBox("Entity info")
|
|
||||||
entity_info_grp.setLayout(QVBoxLayout())
|
|
||||||
entity_info_grp.layout().setContentsMargins(1, 10, 1, 1)
|
|
||||||
self._entity_info = QTextEdit("Entity information")
|
|
||||||
self._file_info.setEnabled(True)
|
|
||||||
self._file_info.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse)
|
|
||||||
self._file_info.setFrameShape(QFrame.NoFrame)
|
|
||||||
self._file_info.setLineWrapMode(QTextEdit.WidgetWidth)
|
|
||||||
entity_info_grp.layout().addWidget(self._entity_info)
|
|
||||||
|
|
||||||
self._splitter = QSplitter(Qt.Horizontal)
|
|
||||||
self._splitter.addWidget(file_info_grp)
|
|
||||||
self._splitter.addWidget(entity_info_grp)
|
|
||||||
self._splitter.addWidget(mdata_grp)
|
|
||||||
self._splitter.setSizes([200, 400, 0])
|
|
||||||
self._splitter.setStretchFactor(0, 0)
|
|
||||||
self._splitter.setStretchFactor(1, 1)
|
|
||||||
self._splitter.setStretchFactor(2, 1)
|
|
||||||
|
|
||||||
self.layout().addWidget(self._splitter)
|
|
||||||
|
|
||||||
|
|
||||||
def setFileInfo(self, file_info):
|
|
||||||
if file_info is not None:
|
|
||||||
self._file_info.setText(file_info.toHtml())
|
|
||||||
|
|
||||||
def setEntityInfo(self, entity_info):
|
|
||||||
if entity_info is None or not isinstance(entity_info, ItemDescriptor):
|
|
||||||
self._splitter.setSizes([200, 400, 0])
|
|
||||||
self._entity_info.setText("")
|
|
||||||
self._metadata_tree.setModel(None)
|
|
||||||
return
|
|
||||||
|
|
||||||
if entity_info.metadata_id is not None:
|
|
||||||
self._splitter.setSizes([200, 400, 400])
|
|
||||||
else:
|
|
||||||
self._splitter.setSizes([200, 400, 0])
|
|
||||||
self._entity_info.setText(entity_info.to_html())
|
|
||||||
metadata_model = TreeModel(self._file_handler, TreeType.Metadata, root_section_id=entity_info.metadata_id)
|
|
||||||
self._metadata_tree.setModel(metadata_model)
|
|
||||||
for i in range(metadata_model.columnCount(None)):
|
|
||||||
self._metadata_tree.resizeColumnToContents(i)
|
|
||||||
|
|
||||||
|
|
||||||
class SplashScreen(QWidget):
|
|
||||||
keyPressed = pyqtSignal(int)
|
|
||||||
|
|
||||||
def __init__(self, parent=None) -> None:
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
#self.setStyleSheet("background-color: white;")
|
|
||||||
|
|
||||||
layout = QGridLayout()
|
|
||||||
layout.setColumnStretch(0, 2)
|
|
||||||
layout.setColumnStretch(1, 1)
|
|
||||||
layout.setColumnStretch(2, 1)
|
|
||||||
layout.setColumnStretch(3, 1)
|
|
||||||
layout.setColumnStretch(4, 2)
|
|
||||||
|
|
||||||
layout.setRowStretch(0, 1)
|
|
||||||
layout.setRowStretch(1, 0)
|
|
||||||
layout.setRowStretch(2, 1)
|
|
||||||
layout.setRowStretch(3, 2)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
label = QLabel()
|
|
||||||
label.setPixmap(QPixmap(os.path.join(cnst.ICONS_FOLDER, "nixview_transparent.png")))
|
|
||||||
label.setMaximumWidth(300)
|
|
||||||
label.setAlignment(Qt.AlignCenter)
|
|
||||||
layout.addWidget(label, 1, 1, 1, 3, Qt.AlignCenter)
|
|
||||||
|
|
||||||
frame = QFrame()
|
|
||||||
l = QVBoxLayout()
|
|
||||||
l.addWidget(QLabel("Recently opened files:"))
|
|
||||||
self._file_list = QListWidget(self)
|
|
||||||
self._file_list.setSelectionMode(QAbstractItemView.SingleSelection)
|
|
||||||
self._file_list.itemClicked.connect(self._on_file_clicked)
|
|
||||||
self._file_list.setFrameShape(QFrame.NoFrame)
|
|
||||||
self.keyPressed.connect(self._on_key_pressed)
|
|
||||||
l.addWidget(self._file_list)
|
|
||||||
frame.setLayout(l)
|
|
||||||
layout.addWidget(frame, 3, 1, 1, 3)
|
|
||||||
self._file_map = {}
|
|
||||||
self._read_recent_files()
|
|
||||||
|
|
||||||
def keyPressEvent(self, event):
|
|
||||||
super(SplashScreen, self).keyPressEvent(event)
|
|
||||||
if event.key() == Qt.Key_Return:
|
|
||||||
self.keyPressed.emit(event.key())
|
|
||||||
|
|
||||||
def _create_short_filename(self, original, index, max_len=40):
|
|
||||||
short = original
|
|
||||||
parts = original.split(os.sep)
|
|
||||||
if len(parts) == 1:
|
|
||||||
short = "%i: %s" % (index, short[:max_len])
|
|
||||||
else:
|
|
||||||
post = parts[-1]
|
|
||||||
if len(post) > max_len - 4:
|
|
||||||
post = post[:max_len - 4]
|
|
||||||
short = str("%i: " % index) + "... " + post
|
|
||||||
else:
|
|
||||||
short = str("%i: " % index) + " ... ".join([original[:max_len - len(post) - 4], post])
|
|
||||||
return short
|
|
||||||
|
|
||||||
def _read_recent_files(self):
|
|
||||||
settings = QSettings(cnst.organization, cnst.application)
|
|
||||||
filenames = settings.value(cnst.settings_recent_files_key, [])
|
|
||||||
del settings
|
|
||||||
|
|
||||||
for i, f in enumerate(filenames):
|
|
||||||
shortname = self._create_short_filename(f, i + 1, max_len=38)
|
|
||||||
self._file_map[shortname] = f
|
|
||||||
item = QListWidgetItem(shortname)
|
|
||||||
item.setToolTip(f)
|
|
||||||
self._file_list.addItem(item)
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self._file_list.clear()
|
|
||||||
self._read_recent_files()
|
|
||||||
|
|
||||||
def _on_file_clicked(self, item):
|
|
||||||
comm.communicator.open_recent.emit(self._file_map[item.text()])
|
|
||||||
|
|
||||||
def _on_key_pressed(self, key):
|
|
||||||
item = self._file_list.currentItem()
|
|
||||||
if item is not None and key == Qt.Key_Return:
|
|
||||||
comm.communicator.open_recent.emit(self._file_map[item.text()])
|
|
||||||
|
141
nixview/ui/filescreen.py
Normal file
141
nixview/ui/filescreen.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
from PyQt5.QtWidgets import QComboBox, QFrame, QGroupBox, QHBoxLayout, QLabel, QSplitter, QTextEdit, QVBoxLayout, QWidget
|
||||||
|
from PyQt5.QtCore import QItemSelectionModel, Qt
|
||||||
|
|
||||||
|
from nixview.file_handler import FileHandler, ItemDescriptor
|
||||||
|
import nixview.communicator as comm
|
||||||
|
import nixview.constants as cnst
|
||||||
|
from nixview.data_models.tree_model import NixTreeView, TreeModel, TreeType
|
||||||
|
|
||||||
|
|
||||||
|
class FileScreen(QWidget):
|
||||||
|
def __init__(self, parent=None) -> None:
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
self._file_handler = FileHandler()
|
||||||
|
|
||||||
|
vbox = QVBoxLayout()
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
main_splitter = QSplitter(Qt.Vertical)
|
||||||
|
self.layout().addWidget(main_splitter)
|
||||||
|
|
||||||
|
self._info = EntityInfo(self)
|
||||||
|
main_splitter.addWidget(self._info)
|
||||||
|
|
||||||
|
self._data_tree = NixTreeView(self)
|
||||||
|
|
||||||
|
self._tree_type_combo = QComboBox()
|
||||||
|
self._tree_type_combo.adjustSize()
|
||||||
|
self._tree_type_combo.addItems([TreeType.Data.value, TreeType.Full.value, TreeType.Metadata.value])
|
||||||
|
self._tree_type_combo.currentTextChanged.connect(self.update)
|
||||||
|
|
||||||
|
hbox = QHBoxLayout()
|
||||||
|
hbox.addWidget(QLabel("Tree type:"))
|
||||||
|
hbox.addWidget(self._tree_type_combo)
|
||||||
|
hbox.addStretch()
|
||||||
|
data_group = QGroupBox("Data")
|
||||||
|
data_vbox = QVBoxLayout()
|
||||||
|
data_vbox.setContentsMargins(1, 10, 1, 1)
|
||||||
|
|
||||||
|
data_vbox.addLayout(hbox)
|
||||||
|
data_vbox.addWidget(self._data_tree)
|
||||||
|
data_group.setLayout(data_vbox)
|
||||||
|
|
||||||
|
main_splitter.addWidget(data_group)
|
||||||
|
main_splitter.setSizes([200, 600])
|
||||||
|
vbox.addWidget(main_splitter)
|
||||||
|
|
||||||
|
def dataTreeSelection(self, current_index, last_index):
|
||||||
|
if not current_index.isValid():
|
||||||
|
return
|
||||||
|
item = current_index.internalPointer()
|
||||||
|
comm.communicator.item_selected.emit(item)
|
||||||
|
self._info.setEntityInfo(item.node_descriptor)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
tt = TreeType.Data
|
||||||
|
if self._tree_type_combo.currentText() == TreeType.Data.value:
|
||||||
|
tt = TreeType.Data
|
||||||
|
elif self._tree_type_combo.currentText() == TreeType.Full.value:
|
||||||
|
tt = TreeType.Full
|
||||||
|
elif self._tree_type_combo.currentText() == TreeType.Metadata.value:
|
||||||
|
tt = TreeType.Metadata
|
||||||
|
self._info.setEntityInfo(None)
|
||||||
|
data_model = TreeModel(self._file_handler, tt)
|
||||||
|
self._data_tree.setModel(data_model)
|
||||||
|
selection_model = QItemSelectionModel(data_model)
|
||||||
|
self._data_tree.setSelectionModel(selection_model)
|
||||||
|
selection_model.currentChanged.connect(self.dataTreeSelection)
|
||||||
|
for i in range(data_model.columnCount(None)):
|
||||||
|
self._data_tree.resizeColumnToContents(i)
|
||||||
|
self._info.setFileInfo(self._file_handler.file_descriptor)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EntityInfo(QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
self._file_handler = FileHandler()
|
||||||
|
self.setLayout(QHBoxLayout())
|
||||||
|
|
||||||
|
self._metadata_tree = NixTreeView()
|
||||||
|
|
||||||
|
mdata_grp = QGroupBox("Metadata")
|
||||||
|
mdata_grp.setLayout(QVBoxLayout())
|
||||||
|
mdata_grp.layout().setContentsMargins(1, 10, 1, 1)
|
||||||
|
mdata_grp.layout().addWidget(self._metadata_tree)
|
||||||
|
|
||||||
|
file_info_grp = QGroupBox("File info")
|
||||||
|
file_info_grp.setLayout(QVBoxLayout())
|
||||||
|
file_info_grp.layout().setContentsMargins(1, 10, 1, 1)
|
||||||
|
self._file_info = QTextEdit("File information")
|
||||||
|
self._file_info.setEnabled(True)
|
||||||
|
self._file_info.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse)
|
||||||
|
self._file_info.setFrameShape(QFrame.NoFrame)
|
||||||
|
self._file_info.setLineWrapMode(QTextEdit.WidgetWidth)
|
||||||
|
file_info_grp.layout().addWidget(self._file_info)
|
||||||
|
|
||||||
|
entity_info_grp = QGroupBox("Entity info")
|
||||||
|
entity_info_grp.setLayout(QVBoxLayout())
|
||||||
|
entity_info_grp.layout().setContentsMargins(1, 10, 1, 1)
|
||||||
|
self._entity_info = QTextEdit("Entity information")
|
||||||
|
self._file_info.setEnabled(True)
|
||||||
|
self._file_info.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse)
|
||||||
|
self._file_info.setFrameShape(QFrame.NoFrame)
|
||||||
|
self._file_info.setLineWrapMode(QTextEdit.WidgetWidth)
|
||||||
|
entity_info_grp.layout().addWidget(self._entity_info)
|
||||||
|
|
||||||
|
self._splitter = QSplitter(Qt.Horizontal)
|
||||||
|
self._splitter.addWidget(file_info_grp)
|
||||||
|
self._splitter.addWidget(entity_info_grp)
|
||||||
|
self._splitter.addWidget(mdata_grp)
|
||||||
|
self._splitter.setSizes([200, 400, 0])
|
||||||
|
self._splitter.setStretchFactor(0, 0)
|
||||||
|
self._splitter.setStretchFactor(1, 1)
|
||||||
|
self._splitter.setStretchFactor(2, 1)
|
||||||
|
|
||||||
|
self.layout().addWidget(self._splitter)
|
||||||
|
|
||||||
|
|
||||||
|
def setFileInfo(self, file_info):
|
||||||
|
if file_info is not None:
|
||||||
|
self._file_info.setText(file_info.toHtml())
|
||||||
|
|
||||||
|
def setEntityInfo(self, entity_info):
|
||||||
|
if entity_info is None or not isinstance(entity_info, ItemDescriptor):
|
||||||
|
self._splitter.setSizes([200, 400, 0])
|
||||||
|
self._entity_info.setText("")
|
||||||
|
self._metadata_tree.setModel(None)
|
||||||
|
return
|
||||||
|
|
||||||
|
if entity_info.metadata_id is not None:
|
||||||
|
self._splitter.setSizes([200, 400, 400])
|
||||||
|
else:
|
||||||
|
self._splitter.setSizes([200, 400, 0])
|
||||||
|
self._entity_info.setText(entity_info.to_html())
|
||||||
|
metadata_model = TreeModel(self._file_handler, TreeType.Metadata, root_section_id=entity_info.metadata_id)
|
||||||
|
self._metadata_tree.setModel(metadata_model)
|
||||||
|
for i in range(metadata_model.columnCount(None)):
|
||||||
|
self._metadata_tree.resizeColumnToContents(i)
|
@ -1,36 +0,0 @@
|
|||||||
from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QVBoxLayout, QWidget
|
|
||||||
from PyQt5.QtCore import QObject, pyqtSignal, Qt
|
|
||||||
import matplotlib
|
|
||||||
matplotlib.use('Qt5Agg')
|
|
||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
|
|
||||||
from matplotlib.figure import Figure
|
|
||||||
|
|
||||||
class MplCanvas(FigureCanvasQTAgg):
|
|
||||||
|
|
||||||
def __init__(self, parent=None, width=5, height=4, dpi=100):
|
|
||||||
fig = Figure(figsize=(width, height), dpi=dpi)
|
|
||||||
self.axes = fig.add_subplot(111)
|
|
||||||
super(MplCanvas, self).__init__(fig)
|
|
||||||
|
|
||||||
|
|
||||||
class PlotScreen(QWidget):
|
|
||||||
close_signal = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self, parent) -> None:
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
sc = MplCanvas(self, width=5, height=4, dpi=100)
|
|
||||||
sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])
|
|
||||||
|
|
||||||
self.setLayout(QVBoxLayout())
|
|
||||||
self.layout().addWidget(sc)
|
|
||||||
|
|
||||||
close_btn = QPushButton("close")
|
|
||||||
close_btn.clicked.connect(self.on_close)
|
|
||||||
|
|
||||||
self.layout().addWidget(close_btn)
|
|
||||||
|
|
||||||
def on_close(self):
|
|
||||||
self.close_signal.emit()
|
|
||||||
|
|
||||||
def plot(self, item):
|
|
||||||
print("plot!", item)
|
|
343
nixview/ui/plotscreen.py
Normal file
343
nixview/ui/plotscreen.py
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QVBoxLayout, QWidget
|
||||||
|
from PyQt5.QtCore import QObject, pyqtSignal, Qt
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('Qt5Agg')
|
||||||
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
|
||||||
|
from matplotlib.figure import Figure
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.widgets import Slider
|
||||||
|
import nixio as nix
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def create_label(entity):
|
||||||
|
label = ""
|
||||||
|
if hasattr(entity, "label"):
|
||||||
|
label += (entity.label if entity.label is not None else "")
|
||||||
|
if len(label) == 0 and hasattr(entity, "name"):
|
||||||
|
label += entity.name
|
||||||
|
if hasattr(entity, "unit") and entity.unit is not None:
|
||||||
|
label += " [%s]" % entity.unit
|
||||||
|
return label
|
||||||
|
|
||||||
|
|
||||||
|
class Plotter(object):
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
class EventPlotter(Plotter):
|
||||||
|
|
||||||
|
def __init__(self, data_array, xdim=-1):
|
||||||
|
self.array = data_array
|
||||||
|
self.sc = None
|
||||||
|
self.dim_count = len(data_array.dimensions)
|
||||||
|
if xdim == -1:
|
||||||
|
self.xdim = guess_best_xdim(self.array)
|
||||||
|
elif xdim > 1:
|
||||||
|
raise ValueError("EventPlotter: xdim is larger than 2! "
|
||||||
|
"Cannot plot that kind of data")
|
||||||
|
else:
|
||||||
|
self.xdim = xdim
|
||||||
|
|
||||||
|
def plot(self, axis=None):
|
||||||
|
if axis is None:
|
||||||
|
self.fig = plt.figure(figsize=[5.5, 2.])
|
||||||
|
self.axis = self.fig.add_axes([0.15, .2, 0.8, 0.75])
|
||||||
|
self.axis.set_title(self.array.name)
|
||||||
|
else:
|
||||||
|
self.fig = axis.figure
|
||||||
|
self.axis = axis
|
||||||
|
if len(self.array.dimensions) == 1:
|
||||||
|
return self.plot_1d()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def plot_1d(self):
|
||||||
|
data = self.array[:]
|
||||||
|
xlabel = create_label(self.array.dimensions[self.xdim])
|
||||||
|
dim = self.array.dimensions[self.xdim]
|
||||||
|
if dim.dimension_type == nix.DimensionType.Range and not dim.is_alias:
|
||||||
|
ylabel = create_label(self.array)
|
||||||
|
else:
|
||||||
|
ylabel = ""
|
||||||
|
self.sc = self.axis.scatter(data, np.ones(data.shape))
|
||||||
|
self.axis.set_ylim([0.5, 1.5])
|
||||||
|
self.axis.set_yticks([1.])
|
||||||
|
self.axis.set_yticklabels([])
|
||||||
|
self.axis.set_xlabel(xlabel)
|
||||||
|
self.axis.set_ylabel(ylabel)
|
||||||
|
return self.axis
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryPlotter(Plotter):
|
||||||
|
|
||||||
|
def __init__(self, data_array, xdim=-1):
|
||||||
|
self.array = data_array
|
||||||
|
self.bars = []
|
||||||
|
if xdim == -1:
|
||||||
|
self.xdim = guess_best_xdim(self.array)
|
||||||
|
elif xdim > 2:
|
||||||
|
raise ValueError("CategoryPlotter: xdim is larger than 2! "
|
||||||
|
"Cannot plot that kind of data")
|
||||||
|
else:
|
||||||
|
self.xdim = xdim
|
||||||
|
|
||||||
|
def plot(self, axis=None):
|
||||||
|
if axis is None:
|
||||||
|
self.fig = plt.figure()
|
||||||
|
self.axis = self.fig.add_axes([0.15, .2, 0.8, 0.75])
|
||||||
|
self.axis.set_title(self.array.name)
|
||||||
|
else:
|
||||||
|
self.fig = axis.figure
|
||||||
|
self.axis = axis
|
||||||
|
if len(self.array.dimensions) == 1:
|
||||||
|
return self.plot_1d()
|
||||||
|
elif len(self.array.dimensions) == 2:
|
||||||
|
return self.plot_2d()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def plot_1d(self):
|
||||||
|
data = self.array[:]
|
||||||
|
dim = self.array.dimensions[self.xdim]
|
||||||
|
if dim.dimension_type == nix.DimensionType.Set:
|
||||||
|
categories = list(dim.labels)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
if categories is None:
|
||||||
|
categories = ["Cat-%i" % i for i in range(len(data))]
|
||||||
|
ylabel = create_label(self.array)
|
||||||
|
if len(categories) == 0:
|
||||||
|
raise ValueError("Cannot plot a bar chart without any labels")
|
||||||
|
self.bars.append(self.axis.bar(range(1, len(categories)+1), data,
|
||||||
|
tick_label=categories))
|
||||||
|
self.axis.set_ylabel(ylabel)
|
||||||
|
return self.axis
|
||||||
|
|
||||||
|
def plot_2d(self):
|
||||||
|
data = self.array[:]
|
||||||
|
if self.xdim == 1:
|
||||||
|
data = data.T
|
||||||
|
|
||||||
|
dim = self.array.dimensions[self.xdim]
|
||||||
|
if dim.dimension_type == nix.DimensionType.Set:
|
||||||
|
categories = list(dim.labels)
|
||||||
|
if len(categories) == 0:
|
||||||
|
categories = ["Cat-%i" % i for i in range(data.shape[self.xdim])]
|
||||||
|
|
||||||
|
dim = self.array.dimensions[1-self.xdim]
|
||||||
|
if dim.dimension_type == nix.DimensionType.Set:
|
||||||
|
series_names = list(dim.labels)
|
||||||
|
if len(series_names) == 0:
|
||||||
|
series_names = ["Series-%i" % i
|
||||||
|
for i in range(data.shape[1-self.xdim])]
|
||||||
|
|
||||||
|
bar_width = 1/data.shape[1] * 0.75
|
||||||
|
for i in range(data.shape[1]):
|
||||||
|
x_values = np.arange(data.shape[0]) + i * bar_width
|
||||||
|
self.bars.append(self.axis.bar(x_values, data[:, i],
|
||||||
|
width=bar_width,
|
||||||
|
align="center")[0])
|
||||||
|
self.axis.set_xticks(np.arange(data.shape[0]) +
|
||||||
|
data.shape[1] * bar_width/2)
|
||||||
|
self.axis.set_xticklabels(categories)
|
||||||
|
self.axis.legend(self.bars, series_names, loc=1)
|
||||||
|
return self.axis
|
||||||
|
|
||||||
|
|
||||||
|
class ImagePlotter(Plotter):
|
||||||
|
|
||||||
|
def __init__(self, data_array, xdim=-1):
|
||||||
|
self.array = data_array
|
||||||
|
self.image = None
|
||||||
|
|
||||||
|
def plot(self, axis=None):
|
||||||
|
dim_count = len(self.array.dimensions)
|
||||||
|
if axis is None:
|
||||||
|
self.fig = plt.figure()
|
||||||
|
self.axis = self.fig.add_axes([0.15, .2, 0.8, 0.75])
|
||||||
|
self.axis.set_title(self.array.name)
|
||||||
|
else:
|
||||||
|
self.fig = axis.figure
|
||||||
|
self.axis = axis
|
||||||
|
if dim_count == 2:
|
||||||
|
return self.plot_2d()
|
||||||
|
elif dim_count == 3:
|
||||||
|
return self.plot_3d()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def plot_2d(self):
|
||||||
|
data = self.array[:]
|
||||||
|
x = self.array.dimensions[0].axis(data.shape[0])
|
||||||
|
y = self.array.dimensions[1].axis(data.shape[1])
|
||||||
|
xlabel = create_label(self.array.dimensions[0])
|
||||||
|
ylabel = create_label(self.array.dimensions[1])
|
||||||
|
self.image = self.axis.imshow(data, extent=[x[0], x[-1], y[0], y[-1]])
|
||||||
|
self.axis.set_xlabel(xlabel)
|
||||||
|
self.axis.set_ylabel(ylabel)
|
||||||
|
self.axis.set
|
||||||
|
return self.axis
|
||||||
|
|
||||||
|
def plot_3d(self):
|
||||||
|
if self.array.shape[2] > 3:
|
||||||
|
print("cannot plot 3d data with more than 3 channels "
|
||||||
|
"in the third dim")
|
||||||
|
return None
|
||||||
|
return self.plot_2d()
|
||||||
|
|
||||||
|
|
||||||
|
class LinePlotter(Plotter):
|
||||||
|
|
||||||
|
def __init__(self, data_array, xdim=-1):
|
||||||
|
self.array = data_array
|
||||||
|
self.lines = []
|
||||||
|
self.dim_count = len(data_array.dimensions)
|
||||||
|
if xdim == -1:
|
||||||
|
self.xdim = guess_best_xdim(self.array)
|
||||||
|
elif xdim > 2:
|
||||||
|
raise ValueError("LinePlotter: xdim is larger than 2! "
|
||||||
|
"Cannot plot that kind of data")
|
||||||
|
else:
|
||||||
|
self.xdim = xdim
|
||||||
|
self.fig = None
|
||||||
|
self.axis = None
|
||||||
|
|
||||||
|
def plot(self, axis=None, maxpoints=100000):
|
||||||
|
self.maxpoints = maxpoints
|
||||||
|
if axis is None:
|
||||||
|
self.fig = plt.figure()
|
||||||
|
self.axis = self.fig.add_axes([0.15, .2, 0.8, 0.75])
|
||||||
|
self.axis.set_title(self.array.name)
|
||||||
|
self.__add_slider()
|
||||||
|
else:
|
||||||
|
self.axis = axis
|
||||||
|
|
||||||
|
dim_count = len(self.array.dimensions)
|
||||||
|
if dim_count > 2:
|
||||||
|
return
|
||||||
|
if dim_count == 1:
|
||||||
|
return self.plot_array_1d()
|
||||||
|
else:
|
||||||
|
return self.plot_array_2d()
|
||||||
|
|
||||||
|
def __add_slider(self):
|
||||||
|
steps = self.array.shape[self.xdim] / self.maxpoints
|
||||||
|
slider_ax = self.fig.add_axes([0.15, 0.025, 0.8, 0.025])
|
||||||
|
self.slider = Slider(slider_ax, 'Slider', 1., steps, valinit=1.,
|
||||||
|
valstep=0.25)
|
||||||
|
self.slider.on_changed(self.__update)
|
||||||
|
|
||||||
|
def __update(self, val):
|
||||||
|
if len(self.lines) > 0:
|
||||||
|
minimum = val * self.maxpoints - self.maxpoints
|
||||||
|
start = minimum if minimum > 0 else 0
|
||||||
|
end = val * self.maxpoints
|
||||||
|
self.__draw(start, end)
|
||||||
|
self.fig.canvas.draw_idle()
|
||||||
|
|
||||||
|
def __draw(self, start, end):
|
||||||
|
if self.dim_count == 1:
|
||||||
|
self.__draw_1d(start, end)
|
||||||
|
else:
|
||||||
|
self.__draw_2d(start, end)
|
||||||
|
|
||||||
|
def __draw_1d(self, start, end):
|
||||||
|
if start < 0:
|
||||||
|
start = 0
|
||||||
|
if end > self.array.shape[self.xdim]:
|
||||||
|
end = self.array.shape[self.xdim]
|
||||||
|
|
||||||
|
y = self.array[int(start):int(end)]
|
||||||
|
dim = self.array.dimensions[self.xdim]
|
||||||
|
x = np.asarray(dim.axis(len(y), int(start)))
|
||||||
|
|
||||||
|
if len(self.lines) == 0:
|
||||||
|
l, = self.axis.plot(x, y, label=self.array.name)
|
||||||
|
self.lines.append(l)
|
||||||
|
else:
|
||||||
|
self.lines[0].set_ydata(y)
|
||||||
|
self.lines[0].set_xdata(x)
|
||||||
|
|
||||||
|
self.axis.set_xlim([x[0], x[-1]])
|
||||||
|
|
||||||
|
def __draw_2d(self, start, end):
|
||||||
|
if start < 0:
|
||||||
|
start = 0
|
||||||
|
if end > self.array.shape[self.xdim]:
|
||||||
|
end = self.array.shape[self.xdim]
|
||||||
|
|
||||||
|
x_dimension = self.array.dimensions[self.xdim]
|
||||||
|
x = np.asarray(x_dimension.axis(int(end-start), start))
|
||||||
|
y_dimension = self.array.dimensions[1-self.xdim]
|
||||||
|
labels = y_dimension.labels
|
||||||
|
if len(labels) == 0:
|
||||||
|
labels = list(map(str, range(self.array.shape[1-self.xdim])))
|
||||||
|
|
||||||
|
for i, l in enumerate(labels):
|
||||||
|
if (self.xdim == 0):
|
||||||
|
y = self.array[int(start):int(end), i]
|
||||||
|
else:
|
||||||
|
y = self.array[i, int(start):int(end)]
|
||||||
|
|
||||||
|
if len(self.lines) <= i:
|
||||||
|
ll, = self.axis.plot(x, y, label=l)
|
||||||
|
self.lines.append(ll)
|
||||||
|
else:
|
||||||
|
self.lines[i].set_ydata(y)
|
||||||
|
self.lines[i].set_xdata(x)
|
||||||
|
|
||||||
|
self.axis.set_xlim([x[0], x[-1]])
|
||||||
|
|
||||||
|
def plot_array_1d(self):
|
||||||
|
self.__draw_1d(0, self.maxpoints)
|
||||||
|
xlabel = create_label(self.array.dimensions[self.xdim])
|
||||||
|
ylabel = create_label(self.array)
|
||||||
|
self.axis.set_xlabel(xlabel)
|
||||||
|
self.axis.set_ylabel(ylabel)
|
||||||
|
return self.axis
|
||||||
|
|
||||||
|
def plot_array_2d(self):
|
||||||
|
self.__draw_2d(0, self.maxpoints)
|
||||||
|
xlabel = create_label(self.array.dimensions[self.xdim])
|
||||||
|
ylabel = create_label(self.array)
|
||||||
|
self.axis.set_xlabel(xlabel)
|
||||||
|
self.axis.set_ylabel(ylabel)
|
||||||
|
self.axis.legend(loc=1)
|
||||||
|
return self.axis
|
||||||
|
|
||||||
|
|
||||||
|
class MplCanvas(FigureCanvasQTAgg):
|
||||||
|
|
||||||
|
def __init__(self, parent=None, width=5, height=4, dpi=100):
|
||||||
|
fig = Figure(figsize=(width, height), dpi=dpi)
|
||||||
|
self.axes = fig.add_subplot(111)
|
||||||
|
super(MplCanvas, self).__init__(fig)
|
||||||
|
|
||||||
|
|
||||||
|
class PlotScreen(QWidget):
|
||||||
|
close_signal = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent) -> None:
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
sc = MplCanvas(self, width=5, height=4, dpi=100)
|
||||||
|
sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])
|
||||||
|
|
||||||
|
self.setLayout(QVBoxLayout())
|
||||||
|
self.layout().addWidget(sc)
|
||||||
|
|
||||||
|
close_btn = QPushButton("close")
|
||||||
|
close_btn.clicked.connect(self.on_close)
|
||||||
|
|
||||||
|
self.layout().addWidget(close_btn)
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
self.close_signal.emit()
|
||||||
|
|
||||||
|
def plot(self, item):
|
||||||
|
print("plot!", item)
|
91
nixview/ui/splashscreen.py
Normal file
91
nixview/ui/splashscreen.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import os
|
||||||
|
from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel, QFrame, QVBoxLayout, QListWidget, QAbstractItemView, QListWidgetItem
|
||||||
|
from PyQt5.QtGui import QPixmap
|
||||||
|
from PyQt5.QtCore import Qt, QSettings, pyqtSignal
|
||||||
|
|
||||||
|
import nixview.communicator as comm
|
||||||
|
import nixview.constants as cnst
|
||||||
|
|
||||||
|
|
||||||
|
class SplashScreen(QWidget):
|
||||||
|
keyPressed = pyqtSignal(int)
|
||||||
|
|
||||||
|
def __init__(self, parent=None) -> None:
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
#self.setStyleSheet("background-color: white;")
|
||||||
|
|
||||||
|
layout = QGridLayout()
|
||||||
|
layout.setColumnStretch(0, 2)
|
||||||
|
layout.setColumnStretch(1, 1)
|
||||||
|
layout.setColumnStretch(2, 1)
|
||||||
|
layout.setColumnStretch(3, 1)
|
||||||
|
layout.setColumnStretch(4, 2)
|
||||||
|
|
||||||
|
layout.setRowStretch(0, 1)
|
||||||
|
layout.setRowStretch(1, 0)
|
||||||
|
layout.setRowStretch(2, 1)
|
||||||
|
layout.setRowStretch(3, 2)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
label = QLabel()
|
||||||
|
label.setPixmap(QPixmap(os.path.join(cnst.ICONS_FOLDER, "nixview_transparent.png")))
|
||||||
|
label.setMaximumWidth(300)
|
||||||
|
label.setAlignment(Qt.AlignCenter)
|
||||||
|
layout.addWidget(label, 1, 1, 1, 3, Qt.AlignCenter)
|
||||||
|
|
||||||
|
frame = QFrame()
|
||||||
|
l = QVBoxLayout()
|
||||||
|
l.addWidget(QLabel("Recently opened files:"))
|
||||||
|
self._file_list = QListWidget(self)
|
||||||
|
self._file_list.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||||
|
self._file_list.itemClicked.connect(self._on_file_clicked)
|
||||||
|
self._file_list.setFrameShape(QFrame.NoFrame)
|
||||||
|
self.keyPressed.connect(self._on_key_pressed)
|
||||||
|
l.addWidget(self._file_list)
|
||||||
|
frame.setLayout(l)
|
||||||
|
layout.addWidget(frame, 3, 1, 1, 3)
|
||||||
|
self._file_map = {}
|
||||||
|
self._read_recent_files()
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
super(SplashScreen, self).keyPressEvent(event)
|
||||||
|
if event.key() == Qt.Key_Return:
|
||||||
|
self.keyPressed.emit(event.key())
|
||||||
|
|
||||||
|
def _create_short_filename(self, original, index, max_len=40):
|
||||||
|
short = original
|
||||||
|
parts = original.split(os.sep)
|
||||||
|
if len(parts) == 1:
|
||||||
|
short = "%i: %s" % (index, short[:max_len])
|
||||||
|
else:
|
||||||
|
post = parts[-1]
|
||||||
|
if len(post) > max_len - 4:
|
||||||
|
post = post[:max_len - 4]
|
||||||
|
short = str("%i: " % index) + "... " + post
|
||||||
|
else:
|
||||||
|
short = str("%i: " % index) + " ... ".join([original[:max_len - len(post) - 4], post])
|
||||||
|
return short
|
||||||
|
|
||||||
|
def _read_recent_files(self):
|
||||||
|
settings = QSettings(cnst.organization, cnst.application)
|
||||||
|
filenames = settings.value(cnst.settings_recent_files_key, [])
|
||||||
|
del settings
|
||||||
|
|
||||||
|
for i, f in enumerate(filenames):
|
||||||
|
shortname = self._create_short_filename(f, i + 1, max_len=38)
|
||||||
|
self._file_map[shortname] = f
|
||||||
|
item = QListWidgetItem(shortname)
|
||||||
|
item.setToolTip(f)
|
||||||
|
self._file_list.addItem(item)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._file_list.clear()
|
||||||
|
self._read_recent_files()
|
||||||
|
|
||||||
|
def _on_file_clicked(self, item):
|
||||||
|
comm.communicator.open_recent.emit(self._file_map[item.text()])
|
||||||
|
|
||||||
|
def _on_key_pressed(self, key):
|
||||||
|
item = self._file_list.currentItem()
|
||||||
|
if item is not None and key == Qt.Key_Return:
|
||||||
|
comm.communicator.open_recent.emit(self._file_map[item.text()])
|
Loading…
Reference in New Issue
Block a user