Pamhyr2/src/View/Tools/ASubWindow.py

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)