mirror of https://gitlab.com/pamhyr/pamhyr2
630 lines
16 KiB
Python
630 lines
16 KiB
Python
# ASubWindow.py -- Pamhyr
|
|
# Copyright (C) 2023-2024 INRAE
|
|
#
|
|
# This program 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/>.
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
import csv
|
|
import logging
|
|
|
|
from io import StringIO
|
|
from datetime import datetime
|
|
|
|
from tools import trace
|
|
|
|
from PyQt5.QtCore import Qt
|
|
|
|
from PyQt5.QtWidgets import (
|
|
QMainWindow, QApplication, QDesktopWidget,
|
|
QMdiArea, QMdiSubWindow, QDialog,
|
|
QPushButton, QLineEdit, QCheckBox,
|
|
QTimeEdit, QSpinBox, QTextEdit,
|
|
QRadioButton, QComboBox, QFileDialog,
|
|
QMessageBox, QTableView, QAction,
|
|
QDateTimeEdit, QWidget, QPlainTextEdit,
|
|
QLabel, QDoubleSpinBox, QButtonGroup,
|
|
)
|
|
from PyQt5.QtCore import (
|
|
QTime, QDateTime,
|
|
)
|
|
from PyQt5.uic import loadUi
|
|
|
|
from Model.Except import ClipboardFormatError
|
|
|
|
logger = logging.getLogger()
|
|
|
|
|
|
class WindowToolKit(object):
|
|
def __init__(self, parent=None):
|
|
super(WindowToolKit, self).__init__()
|
|
|
|
def copyTableIntoClipboard(self, table):
|
|
stream = StringIO()
|
|
csv.writer(stream, delimiter='\t').writerows(table)
|
|
QApplication.clipboard().setText(stream.getvalue())
|
|
|
|
def parseClipboardTable(self):
|
|
clip = QApplication.clipboard()
|
|
mime = clip.mimeData()
|
|
if 'text/plain' not in mime.formats():
|
|
raise ClipboardFormatError(mime='text/plain')
|
|
|
|
data = mime.data('text/plain').data().decode()
|
|
has_header = csv.Sniffer().has_header(data)
|
|
|
|
header = []
|
|
values = []
|
|
|
|
delimiter = ' '
|
|
if ';' in data:
|
|
delimiter = ';'
|
|
if '\t' in data:
|
|
delimiter = '\t'
|
|
|
|
stream = StringIO(data)
|
|
rows = csv.reader(stream, delimiter=delimiter)
|
|
for ind, row in enumerate(rows):
|
|
if has_header and ind == 0:
|
|
header = row.copy()
|
|
continue
|
|
|
|
values.append(row)
|
|
|
|
return header, values
|
|
|
|
def file_dialog(self, select_file="ExistingFile",
|
|
callback=lambda x: None,
|
|
directory=None,
|
|
default_suffix=None,
|
|
file_filter=None):
|
|
"""Open a new file dialog and send result to callback function
|
|
|
|
Args:
|
|
select_file: Select a file if True, else select a dir
|
|
callback: The callback function with one arguments, files
|
|
selection list
|
|
directory: Defaut directory
|
|
default_suffix: Default file suffix
|
|
file_filter: List of file filter
|
|
Returns:
|
|
The returns of callback
|
|
"""
|
|
options = QFileDialog.Options()
|
|
options |= QFileDialog.DontUseNativeDialog
|
|
|
|
dialog = QFileDialog(self, options=options)
|
|
|
|
if select_file == "Existing_file":
|
|
mode = QFileDialog.FileMode.ExistingFile
|
|
elif select_file == "Directory":
|
|
mode = QFileDialog.FileMode.Directory
|
|
else:
|
|
mode = QFileDialog.FileMode.AnyFile
|
|
|
|
dialog.setFileMode(mode)
|
|
if directory is not None:
|
|
dialog.setDirectory(directory)
|
|
|
|
if select_file != "Directory":
|
|
if default_suffix is not None:
|
|
dialog.setDefaultSuffix(default_suffix)
|
|
|
|
if file_filter is not None:
|
|
dialog.setNameFilters(file_filter)
|
|
|
|
if dialog.exec_():
|
|
file_names = dialog.selectedFiles()
|
|
return callback(file_names)
|
|
|
|
def message_box(self, text: str,
|
|
informative_text: str,
|
|
window_title: str = "Warning"):
|
|
"""Open a new message box
|
|
|
|
Args:
|
|
text: Short text string
|
|
informative_text: Verbose text string
|
|
window_title: Title of message box window
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
msg = QMessageBox(parent=self)
|
|
|
|
msg.setIcon(QMessageBox.Warning)
|
|
msg.setText(text)
|
|
msg.setInformativeText(informative_text)
|
|
msg.setWindowTitle(window_title)
|
|
|
|
msg.findChild(QLabel, "qt_msgbox_label")\
|
|
.setFixedWidth(384)
|
|
|
|
msg.exec_()
|
|
|
|
|
|
class ASubWindowFeatures(object):
|
|
def __init__(self, parent=None):
|
|
super(ASubWindowFeatures, self).__init__()
|
|
|
|
# Commun use features
|
|
|
|
def _qtype_from_component_name(self, name):
|
|
qtype = None
|
|
|
|
if "action" in name:
|
|
qtype = QAction
|
|
elif "lineEdit" in name:
|
|
qtype = QLineEdit
|
|
elif "pushButton" in name:
|
|
qtype = QPushButton
|
|
elif "radioButton" in name:
|
|
qtype = QRadioButton
|
|
elif "tableView" in name:
|
|
qtype = QTableView
|
|
|
|
return qtype
|
|
|
|
def get_label_text(self, name: str):
|
|
"""Get text of label component
|
|
|
|
Args:
|
|
label: The label component name
|
|
|
|
Returns:
|
|
Text
|
|
"""
|
|
return self.find(QLabel, name).text()
|
|
|
|
def set_label_text(self, name: str, text: str):
|
|
"""Set text of label component
|
|
|
|
Args:
|
|
text_edit: The label component name
|
|
text: The text
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QLabel, name).setText(text)
|
|
|
|
def set_line_edit_text(self, name: str, text: str):
|
|
"""Set text of line edit component
|
|
|
|
Args:
|
|
line_edit: The line edit component name
|
|
text: The text
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
try:
|
|
self.find(QLineEdit, name).setText(text)
|
|
except AttributeError as e:
|
|
logger.error(e)
|
|
|
|
def get_line_edit_text(self, name: str):
|
|
"""Get text of line edit component
|
|
|
|
Args:
|
|
line_edit: The line edit component name
|
|
|
|
Returns:
|
|
Text
|
|
"""
|
|
return self.find(QLineEdit, name).text()
|
|
|
|
def set_text_edit_text(self, name: str, text: str):
|
|
"""Set text of text edit component
|
|
|
|
Args:
|
|
text_edit: The text edit component name
|
|
text: The text
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QTextEdit, name).setMarkdown(text)
|
|
|
|
def get_text_edit_text(self, name: str):
|
|
"""Get text of text edit component
|
|
|
|
Args:
|
|
text_edit: The text edit component name
|
|
|
|
Returns:
|
|
Text
|
|
"""
|
|
return self.find(QTextEdit, name).toMarkdown()
|
|
|
|
def set_plaintext_edit_text(self, name: str, text: str):
|
|
"""Set text of text edit component
|
|
|
|
Args:
|
|
text_edit: The text edit component name
|
|
text: The text
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QPlainTextEdit, name).setPlainText(text)
|
|
|
|
def get_plaintext_edit_text(self, name: str):
|
|
"""Get text of text edit component
|
|
|
|
Args:
|
|
text_edit: The text edit component name
|
|
|
|
Returns:
|
|
Text
|
|
"""
|
|
return self.find(QPlainTextEdit, name).toPlainText()
|
|
|
|
def set_check_box(self, name: str, checked: bool):
|
|
"""Set status of checkbox component
|
|
|
|
Args:
|
|
name: The check box component name
|
|
checked: Bool
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QCheckBox, name).setChecked(checked)
|
|
|
|
def get_check_box(self, name: str):
|
|
"""Get status of checkbox component
|
|
|
|
Args:
|
|
name: The check box component name
|
|
|
|
Returns:
|
|
Status of checkbox (bool)
|
|
"""
|
|
return self.find(QCheckBox, name).isChecked()
|
|
|
|
def set_time_edit(self, name: str, time: str):
|
|
"""Set time of timeedit component
|
|
|
|
Args:
|
|
name: The timeedit component name
|
|
time: The new time in format "HH:mm:ss"
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
qtime = QTime.fromString(time)
|
|
self.find(QTimeEdit, name).setTime(qtime)
|
|
|
|
def get_time_edit(self, name: str):
|
|
"""Get time of timeedit component
|
|
|
|
Args:
|
|
name: The timeedit component name
|
|
|
|
Returns:
|
|
The time of timeedit in format "HH:mm:ss"
|
|
"""
|
|
return self.find(QTimeEdit, name).time().toString()
|
|
|
|
def set_spin_box(self, name: str, value: int):
|
|
"""Set value of spinbox component
|
|
|
|
Args:
|
|
name: The spinbox component name
|
|
value: The new value
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QSpinBox, name).setValue(value)
|
|
|
|
def get_spin_box(self, name: str):
|
|
"""Get time of spin box component
|
|
|
|
Args:
|
|
name: The spin box component
|
|
|
|
Returns:
|
|
The value of spin box
|
|
"""
|
|
return self.find(QSpinBox, name).value()
|
|
|
|
def set_double_spin_box(self, name: str, value: int):
|
|
"""Set value of spinbox component
|
|
|
|
Args:
|
|
name: The spinbox component name
|
|
value: The new value
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QDoubleSpinBox, name).setValue(value)
|
|
|
|
def get_double_spin_box(self, name: str):
|
|
"""Get time of spin box component
|
|
|
|
Args:
|
|
name: The spin box component
|
|
|
|
Returns:
|
|
The value of spin box
|
|
"""
|
|
return self.find(QDoubleSpinBox, name).value()
|
|
|
|
def set_action_checkable(self, name: str, checked: bool):
|
|
"""Set value of action
|
|
|
|
Args:
|
|
name: The action component name
|
|
value: The new value
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QAction, name).setChecked(checked)
|
|
|
|
def get_action_checkable(self, name: str):
|
|
"""Get status of action
|
|
|
|
Args:
|
|
name: The action component name
|
|
|
|
Returns:
|
|
The status of action
|
|
"""
|
|
return self.find(QAction, name).isChecked()
|
|
|
|
def set_push_button_checkable(self, name: str, checked: bool):
|
|
"""Set value of push button component
|
|
|
|
Args:
|
|
name: The push button component name
|
|
value: The new value
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QPushButton, name).setChecked(checked)
|
|
|
|
def get_push_button_checkable(self, name: str):
|
|
"""Get status of push button
|
|
|
|
Args:
|
|
name: The push button component name
|
|
|
|
Returns:
|
|
The status of push button
|
|
"""
|
|
return self.find(QPushButton, name).isChecked()
|
|
|
|
def set_radio_button(self, name: str, checked: bool):
|
|
"""Set value of radio button component
|
|
|
|
Args:
|
|
name: The radio button component name
|
|
checked: Checked
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QRadioButton, name).setChecked(checked)
|
|
|
|
def get_radio_button(self, name: str):
|
|
"""Get status of radio button
|
|
|
|
Args:
|
|
name: The radio button component name
|
|
|
|
Returns:
|
|
The status of radio button
|
|
"""
|
|
return self.find(QRadioButton, name).isChecked()
|
|
|
|
def combobox_add_item(self, name: str, item: str):
|
|
"""Add item in combo box
|
|
|
|
Args:
|
|
name: The combo box component name
|
|
item: The item to add
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QComboBox, name).addItem(item)
|
|
|
|
def combobox_add_items(self, name: str, items: str):
|
|
"""Add item in combo box
|
|
|
|
Args:
|
|
name: The combo box component name
|
|
item: The item to add
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
for item in items:
|
|
self.find(QComboBox, name).addItem(item)
|
|
|
|
def set_combobox_text(self, name: str, item: str):
|
|
"""Set current text of combo box
|
|
|
|
Args:
|
|
name: The combo box component name
|
|
item: The item to add
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
self.find(QComboBox, name).setCurrentText(item)
|
|
|
|
def get_combobox_text(self, name: str):
|
|
"""Get current text of combo box
|
|
|
|
Args:
|
|
name: The combo box component name
|
|
|
|
Returns:
|
|
Current text
|
|
"""
|
|
return self.find(QComboBox, name).currentText()
|
|
|
|
def get_datetime_edit(self, name: str):
|
|
"""Get datetime of datetime edit
|
|
|
|
Args:
|
|
name: The datetime edit component name
|
|
|
|
Returns:
|
|
The datetime
|
|
"""
|
|
return self.find(QDateTimeEdit, name).dateTime().toPyDateTime()
|
|
|
|
def set_datetime_edit(self, name: str, date: datetime):
|
|
"""Set datetime of a datetime edit
|
|
|
|
Args:
|
|
name: The datetime edit component name
|
|
date: The new datetime
|
|
|
|
Returns:
|
|
Nothing
|
|
"""
|
|
qdate = QDateTime.fromString(date.isoformat(), "yyyy-MM-ddThh:mm:ss")
|
|
self.find(QDateTimeEdit, name).setDateTime(qdate)
|
|
|
|
def get_checked_id_button_group(self, name: str):
|
|
"""Get current checked button id in a buttonGroup
|
|
|
|
Args:
|
|
name: The buttonGroup component name
|
|
|
|
Returns:
|
|
Current checked id
|
|
"""
|
|
return self.find(QButtonGroup, name).checkedId()
|
|
|
|
|
|
# Top level interface
|
|
|
|
class ASubMainWindow(QMainWindow, ASubWindowFeatures, WindowToolKit):
|
|
def __init__(self, name="", ui="dummy", parent=None, **kwargs):
|
|
super(ASubMainWindow, self).__init__(parent=parent)
|
|
if ui is not None:
|
|
self.ui = loadUi(
|
|
os.path.join(self._get_ui_directory(), f"{ui}.ui"),
|
|
self
|
|
)
|
|
|
|
self.name = name
|
|
self.parent = parent
|
|
if self.parent is not None:
|
|
self.parent.sub_win_add(name, self)
|
|
|
|
def _get_ui_directory(self):
|
|
return os.path.abspath(
|
|
os.path.join(
|
|
os.path.dirname(__file__),
|
|
"..", "ui"
|
|
)
|
|
)
|
|
|
|
def closeEvent(self, event):
|
|
if self.parent is not None:
|
|
self.parent.sub_win_del(self.hash())
|
|
|
|
def find(self, qtype, name):
|
|
"""Find an ui component
|
|
|
|
Args:
|
|
qtype: Type of QT component
|
|
name: Name for component
|
|
|
|
Returns:
|
|
return the component
|
|
"""
|
|
if qtype is None:
|
|
qtype = self._qtype_from_component_name(name)
|
|
|
|
return self.ui.findChild(qtype, name)
|
|
|
|
|
|
class ASubWindow(QDialog, ASubWindowFeatures, WindowToolKit):
|
|
def __init__(self, name="", ui="dummy", parent=None, **kwargs):
|
|
super(ASubWindow, self).__init__(parent=parent)
|
|
self.ui = loadUi(
|
|
os.path.abspath(
|
|
os.path.join(
|
|
os.path.dirname(__file__),
|
|
"..", "ui", f"{ui}.ui"
|
|
)
|
|
),
|
|
self
|
|
)
|
|
self.name = name
|
|
self.parent = parent
|
|
if self.parent is not None:
|
|
self.parent.sub_win_add(name, self)
|
|
|
|
def closeEvent(self, event):
|
|
if self.parent is not None:
|
|
self.parent.sub_win_del(self.hash())
|
|
|
|
def find(self, qtype, name):
|
|
"""Find an ui component
|
|
|
|
Args:
|
|
qtype: Type of QT component
|
|
name: Name for component
|
|
|
|
Returns:
|
|
return the component
|
|
"""
|
|
if qtype is None:
|
|
qtype = self._qtype_from_component_name(name)
|
|
|
|
return self.ui.findChild(qtype, name)
|
|
|
|
|
|
class AWidget(QWidget, ASubWindowFeatures):
|
|
def __init__(self, ui="", parent=None):
|
|
super(AWidget, self).__init__(parent=parent)
|
|
self.ui = loadUi(
|
|
os.path.abspath(
|
|
os.path.join(
|
|
os.path.dirname(__file__),
|
|
"..", "ui", "Widgets", f"{ui}.ui"
|
|
)
|
|
),
|
|
self
|
|
)
|
|
self.parent = parent
|
|
|
|
def find(self, qtype, name):
|
|
"""Find an ui component
|
|
|
|
Args:
|
|
qtype: Type of QT component
|
|
name: Name for component
|
|
|
|
Returns:
|
|
return the component
|
|
"""
|
|
if qtype is None:
|
|
qtype = self._qtype_from_component_name(name)
|
|
|
|
return self.ui.findChild(qtype, name)
|