Module soitool.modules.module_frequency_table
SOI module for radio communications overview.
Expand source code
"""SOI module for radio communications overview."""
from functools import partial
from json import load
from PySide2.QtWidgets import (
QWidget,
QLabel,
QTableWidget,
QPushButton,
QVBoxLayout,
QHBoxLayout,
QCheckBox,
QDialog,
QTableWidgetItem,
QGridLayout,
)
from PySide2.QtCore import Qt
from PySide2.QtGui import QIcon
from soitool.modules.module_base import (
ModuleBase,
HEADLINE_FONT,
SUB_HEADLINE_FONT,
DEFAULT_FONT,
resize_table,
prepare_table_for_pdf_export,
is_event_remove_row,
is_event_add_row,
is_event_edit_module,
)
FIXED_WIDTH = 250
class ColumnsChoicePopup(QDialog):
"""The popup for the choice of columns.
This dialog allows the user to select wich radio types and wich attributes
to the radio types the frequency table shall display.
The possible combinations of radio types and attributes can be customized
through the configuration file associated with the module.
Parameters
----------
columns : dict
The table structure of frequency table.
"""
def __init__(self, columns):
super().__init__()
# Set datastructure from frequency table
self.columns = columns
self.setWindowTitle("Velg kolonner til frekvenstabell")
self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
# Layout for headers and checkboxes
grid = QGridLayout()
grid.setVerticalSpacing(15)
grid.setHorizontalSpacing(30)
grid.setAlignment(Qt.AlignTop | Qt.AlignLeft)
# Creating widget structure
current_column = 0
checkbox_row = 1
for main_header in self.columns.keys():
# Add header
sub_header = QLabel(main_header)
sub_header.setFont(SUB_HEADLINE_FONT)
grid.addWidget(sub_header, checkbox_row - 1, current_column)
# Add checkboxes
grid.addLayout(
get_checkboxes_in_vbox(self.columns[main_header]),
checkbox_row,
current_column,
)
# Button for toggle all checkboxes on/off
checkboxes = grid.itemAtPosition(checkbox_row, current_column)
btn_toggle_all = QPushButton("Veksle alle")
btn_toggle_all.clicked.connect(partial(toggle_all_in, checkboxes))
grid.addWidget(btn_toggle_all, checkbox_row + 1, current_column)
# Increment collumn counter
current_column += 1
# Button for submitting form
btn_done = QPushButton("Bruk")
btn_done.clicked.connect(lambda: self.update_columns_dict(grid))
btn_done.setFixedHeight(50)
# Layout main
vbox = QVBoxLayout()
vbox.addLayout(grid)
vbox.addSpacing(40)
vbox.addWidget(btn_done)
self.setLayout(vbox)
def update_columns_dict(self, grid):
"""Update the table structure dict.
Based on which checkboxes that are checked and unchecked the dict that
holds the table structure (self.columns) gets updated.
Parameters
----------
grid : QGridLayout
The grid holding the checkboxes and headers.
"""
for column_index in range(grid.columnCount()):
main_header = grid.itemAtPosition(0, column_index).widget().text()
checkboxes = grid.itemAtPosition(1, column_index)
for checkbox_index in range(checkboxes.count()):
checkbox = checkboxes.itemAt(checkbox_index).widget()
sub_header = checkbox.text()
self.columns[main_header][sub_header] = checkbox.isChecked()
self.accept()
def get_columns_dict(self):
"""Getter for the frequency table structure dict.
Returns
-------
self.columns : dict
The frequency table structure dict.
"""
return self.columns
def get_checkboxes_in_vbox(sub_column_dict):
"""Create a vbox with checkboxes from dict.
Parameters
----------
sub_column_dict : dict
The dict to create checkboxses of.
Returns
-------
QVBoxLayout
Vbox with checkboxes.
"""
vbox = QVBoxLayout()
vbox.setAlignment(Qt.AlignTop)
for sub_column in sub_column_dict.keys():
checkbox = QCheckBox(sub_column)
checkbox.setChecked(sub_column_dict[sub_column])
vbox.addWidget(checkbox)
return vbox
def toggle_all_in(checkboxes):
"""Toggle all checkboxes in layout.
If some of the checkboxes in the layout are checked, all gets checked.
If all of the checkboxes in the layout are checked, all gets unchecked.
Parameters
----------
checkboxes : QLayout
Layout with checkboxes.
"""
toggle = False
for item_index in range(checkboxes.count()):
if not checkboxes.itemAt(item_index).widget().isChecked():
toggle = True
break
for item_index in range(checkboxes.count()):
checkboxes.itemAt(item_index).widget().setChecked(toggle)
class Meta(type(ModuleBase), type(QWidget)):
"""Used as a metaclass to enable multiple inheritance."""
class FrequencyTableModule(ModuleBase, QWidget, metaclass=Meta):
"""Table with overview of radio communication attributes.
A frequency table is an overview of information needed to establish radio
communication.
# This module includes:
## Components
* Header
* Table with predefined columns
* Buttons for editing module
* Popup (ColumnsChoicePopup)
## Features
* Keyboard shortcuts for editing module
* Buttons only visible when mouse in modules space
* Interface generated from configuration file
* Automatic resizing
* Able to be loaded from data parameter
Parameters
----------
data : dict
Module content, used if not None. See self.get_data().
"""
def __init__(self, data=None):
self.type = FrequencyTableModule.__name__
QWidget.__init__(self)
ModuleBase.__init__(self)
with open(
"soitool/modules/config/module_frequency_table.json",
"r",
encoding="utf-8",
) as config_file:
self.columns = load(config_file)
# Header
self.header = QLabel("FREKVENSTABELL")
self.header.setFont(HEADLINE_FONT)
self.header.setFixedSize(322, 40)
# Table
self.table = self.__create_table()
self.table.cellChanged.connect(self.resize)
# Buttons
self.buttons = self.__create_buttons()
# Layout
layout = QVBoxLayout()
layout.setMargin(0)
layout.setSpacing(0)
layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
layout.addWidget(self.header, alignment=Qt.AlignCenter)
layout.addWidget(self.table)
layout.addWidget(self.buttons)
self.setLayout(layout)
if not data:
self.set_columns()
else:
self.load_data_to_module(data)
# !!!!! MAIN COMPONENTS SETUP !!!!!
def __create_table(self):
"""Create the frequency table itself.
Returns
-------
table : QTableWidget
The frequency table itself.
"""
table = QTableWidget(3, self.get_max_number_of_columns())
table.setFont(DEFAULT_FONT)
table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
table.verticalHeader().hide()
table.horizontalHeader().hide()
self.setStyleSheet("QTableView { gridline-color: black; }")
column = 0
# Set main headers
for main_header in self.columns.keys():
span = len(self.columns[main_header])
if span > 1:
table.setSpan(0, column, 1, span)
item = QTableWidgetItem(main_header)
item.setFlags(item.flags() ^ Qt.ItemIsEditable)
item.setFont(HEADLINE_FONT)
table.setItem(0, column, item)
# Set subheaders
for sub_header in self.columns[main_header].keys():
item = QTableWidgetItem(sub_header)
item.setFlags(item.flags() ^ Qt.ItemIsEditable)
item.setFont(SUB_HEADLINE_FONT)
table.setItem(1, column, item)
column += 1
return table
def __create_buttons(self):
"""Create the buttons for editing table.
Returns
-------
buttons_wrapper : QWidget
Wrapper widget containing the buttons to edit table.
"""
# Button for selecting columns
btn_columns = QPushButton("Kolonner")
btn_columns.setFixedWidth(100)
btn_columns.clicked.connect(self.open_columns_popup)
# Button for adding row
btn_add = QPushButton(" + ")
btn_add.setFixedWidth(50)
btn_add.clicked.connect(self.add_row)
# Buttons for removing row
btn_remove = QPushButton(" - ")
btn_remove.setFixedWidth(50)
btn_remove.clicked.connect(self.remove_row)
# Layout
hbox = QHBoxLayout()
hbox.setSpacing(0)
hbox.setMargin(0)
hbox.addWidget(btn_columns)
hbox.addWidget(btn_add)
hbox.addWidget(btn_remove)
# Wrapping layout in a widget
buttons_wrapper = QWidget()
buttons_wrapper.setFixedWidth(FIXED_WIDTH)
buttons_wrapper.setFixedHeight(33)
buttons_wrapper.setLayout(hbox)
buttons_wrapper.hide()
return buttons_wrapper
# !!!!! EVENT HANDLERS !!!!!
def enterEvent(self, event):
"""Eventhandler for showing buttons when mouse enters widget.
Parameters
----------
event : enterEvent
Called when mouse enter widgets screen space.
"""
self.buttons.show()
self.resize()
QWidget.enterEvent(self, event)
def leaveEvent(self, event):
"""Eventhandler for hiding buttons when mouse leaves widget.
Parameters
----------
event : eneterEvent
Called when mouse leaves widgets screen space.
"""
self.buttons.hide()
self.resize()
QWidget.leaveEvent(self, event)
def keyPressEvent(self, event):
"""Eventhandler for adding and removing rows and columns.
Parameters
----------
event : keyPressEvent
Holds which keys were pressed.
"""
if is_event_remove_row(event):
self.remove_row()
elif is_event_edit_module(event):
self.open_columns_popup()
elif is_event_add_row(event):
self.add_row()
else:
super().keyPressEvent(event)
# !!!!! TABLE OPERATIONS !!!!!
def get_max_number_of_columns(self):
"""Get number of maximum number of columns in table.
Returns
-------
number : int
The max number of columns.
"""
number = 0
for value in self.columns.values():
number += len(value)
return number
def add_row(self):
"""Add column to table."""
if self.table.currentRow() > 0:
self.table.insertRow(self.table.currentRow() + 1)
else:
self.table.setRowCount(self.table.rowCount() + 1)
self.resize()
def remove_row(self):
"""Remove a column and protect header rows."""
if self.table.currentRow() > 1:
self.table.removeRow(self.table.currentRow())
self.resize()
def set_columns(self):
"""Set wich columns to hide and show based on self.columns dict."""
for main_header in self.columns.keys():
for sub_header in self.columns[main_header].keys():
self.table.setColumnHidden(
self.get_column_index_by_headers(main_header, sub_header),
not (self.columns[main_header][sub_header]),
)
# To prevent table to get scrolled out of widget when adding columns
self.table.horizontalScrollBar().setValue(0)
self.resize()
def get_column_index_by_headers(self, main_header, sub_header):
"""Find column index belonging to self.columns dict element.
Parameters
----------
main_header : string
First key in self.columns.
sub_header : string
Second key in self.columns.
Returns
-------
sub_column : int
Index of column.
Raises
------
LookupError
If column not found.
"""
for main_column in range(self.table.columnCount()):
item = self.table.item(0, main_column)
if item is not None and item.text() == main_header:
span = self.table.columnSpan(0, main_column)
for sub_column in range(main_column, main_column + span):
if self.table.item(1, sub_column).text() == sub_header:
return sub_column
raise LookupError(
"self.colums[" + main_header + "][" + sub_header + "]"
)
# !!!!! MODULE METHODS !!!!!
def resize(self):
"""Resize module to content."""
resize_table(self.table)
width = max(self.header.minimumWidth(), self.table.minimumWidth())
if self.buttons.isVisible() and self.buttons.minimumWidth() > width:
width = self.buttons.minimumWidth()
self.setFixedWidth(width)
height = self.header.minimumHeight() + self.table.minimumHeight()
if self.buttons.isVisible():
height += self.buttons.minimumHeight()
self.setFixedHeight(height)
def open_columns_popup(self):
"""Open column edit dialog."""
popup = ColumnsChoicePopup(self.columns)
popup.exec_()
self.columns = popup.get_columns_dict()
self.set_columns()
def load_data_to_module(self, data):
"""Load module content from data.
Parameters
----------
data : dict
Module serialized as a dict.
format = {
main_header: {
sub_header1: [row1, row2, row3],
sub_header2: [row1, row2, row3],
},
main_header2: {...},
}
"""
# Updating wich columns to hide and show
for main_header in self.columns.keys():
if main_header in data.keys():
for sub_header in self.columns[main_header].keys():
if sub_header in data[main_header].keys():
self.columns[main_header][sub_header] = True
else:
self.columns[main_header][sub_header] = False
else:
for sub_header in self.columns[main_header].keys():
self.columns[main_header][sub_header] = False
# Updating column structure according to self.columns
self.set_columns()
# Needed rows
number_of_rows = len(list(data[list(data.keys())[0]].values())[0])
# -1 because one row is already there
self.table.setRowCount(self.table.rowCount() + number_of_rows - 1)
# Adding content to table
for main_header in self.columns.keys():
for sub_header in self.columns[main_header].keys():
if self.columns[main_header][sub_header]:
for row_index in range(2, number_of_rows + 2):
column_index = self.get_column_index_by_headers(
main_header, sub_header
)
current_item = QTableWidgetItem(
data[main_header][sub_header][row_index - 2]
)
self.table.setItem(
row_index, column_index, current_item
)
# !!!!! MODULE BASE METHODS !!!!!
def get_size(self):
"""Get module dimensions.
Returns
-------
tuple
(width, heigth)
"""
self.resize()
return (self.minimumWidth(), self.minimumHeight())
def get_data(self):
"""Serialize the module data.
Returns
-------
data : dict
Table structure and data in a dict
Format:
{
main_header1 : {
sub_header1 : [row1, row2, row3],
sub_header2 : [row1, row2, row3]
},
main_header2 : {
sub_header3 : [row1, row2, row3],
...
},
...
}
"""
data = {}
column_index = 0
# Loop trough every main header
while column_index < self.table.columnCount():
span = self.table.columnSpan(0, column_index)
main_header = self.table.item(0, column_index).text()
sub_data = {}
# Loop trough every sub header under current main header
for sub_column_index in range(column_index, column_index + span):
if not self.table.isColumnHidden(sub_column_index):
sub_header = self.table.item(1, sub_column_index).text()
columns_data = []
# Loop through every row under current sub header
for row_index in range(2, self.table.rowCount()):
item = self.table.item(row_index, sub_column_index)
if item is None:
columns_data.append("")
else:
columns_data.append(item.text())
sub_data[sub_header] = columns_data
if sub_data:
data[main_header] = sub_data
column_index += span
return data
@staticmethod
def get_user_friendly_name():
"""Get user friendly name of module.
Returns
-------
string
User friendly name.
"""
return "Frekvenstabell"
def prepare_for_pdf_export(self):
"""Prepare for PDF-export."""
prepare_table_for_pdf_export(self.table)
@staticmethod
def get_icon():
"""Get module icon.
Returns
-------
QIcon
Icon of the module.
"""
return QIcon("soitool/media/frequencytable.png")
Functions
def get_checkboxes_in_vbox(sub_column_dict)
-
Create a vbox with checkboxes from dict.
Parameters
sub_column_dict
:dict
- The dict to create checkboxses of.
Returns
QVBoxLayout
- Vbox with checkboxes.
Expand source code
def get_checkboxes_in_vbox(sub_column_dict): """Create a vbox with checkboxes from dict. Parameters ---------- sub_column_dict : dict The dict to create checkboxses of. Returns ------- QVBoxLayout Vbox with checkboxes. """ vbox = QVBoxLayout() vbox.setAlignment(Qt.AlignTop) for sub_column in sub_column_dict.keys(): checkbox = QCheckBox(sub_column) checkbox.setChecked(sub_column_dict[sub_column]) vbox.addWidget(checkbox) return vbox
def toggle_all_in(checkboxes)
-
Toggle all checkboxes in layout.
If some of the checkboxes in the layout are checked, all gets checked. If all of the checkboxes in the layout are checked, all gets unchecked.
Parameters
checkboxes
:QLayout
- Layout with checkboxes.
Expand source code
def toggle_all_in(checkboxes): """Toggle all checkboxes in layout. If some of the checkboxes in the layout are checked, all gets checked. If all of the checkboxes in the layout are checked, all gets unchecked. Parameters ---------- checkboxes : QLayout Layout with checkboxes. """ toggle = False for item_index in range(checkboxes.count()): if not checkboxes.itemAt(item_index).widget().isChecked(): toggle = True break for item_index in range(checkboxes.count()): checkboxes.itemAt(item_index).widget().setChecked(toggle)
Classes
class ColumnsChoicePopup (columns)
-
The popup for the choice of columns.
This dialog allows the user to select wich radio types and wich attributes to the radio types the frequency table shall display.
The possible combinations of radio types and attributes can be customized through the configuration file associated with the module.
Parameters
columns
:dict
- The table structure of frequency table.
Expand source code
class ColumnsChoicePopup(QDialog): """The popup for the choice of columns. This dialog allows the user to select wich radio types and wich attributes to the radio types the frequency table shall display. The possible combinations of radio types and attributes can be customized through the configuration file associated with the module. Parameters ---------- columns : dict The table structure of frequency table. """ def __init__(self, columns): super().__init__() # Set datastructure from frequency table self.columns = columns self.setWindowTitle("Velg kolonner til frekvenstabell") self.setWindowFlag(Qt.WindowContextHelpButtonHint, False) # Layout for headers and checkboxes grid = QGridLayout() grid.setVerticalSpacing(15) grid.setHorizontalSpacing(30) grid.setAlignment(Qt.AlignTop | Qt.AlignLeft) # Creating widget structure current_column = 0 checkbox_row = 1 for main_header in self.columns.keys(): # Add header sub_header = QLabel(main_header) sub_header.setFont(SUB_HEADLINE_FONT) grid.addWidget(sub_header, checkbox_row - 1, current_column) # Add checkboxes grid.addLayout( get_checkboxes_in_vbox(self.columns[main_header]), checkbox_row, current_column, ) # Button for toggle all checkboxes on/off checkboxes = grid.itemAtPosition(checkbox_row, current_column) btn_toggle_all = QPushButton("Veksle alle") btn_toggle_all.clicked.connect(partial(toggle_all_in, checkboxes)) grid.addWidget(btn_toggle_all, checkbox_row + 1, current_column) # Increment collumn counter current_column += 1 # Button for submitting form btn_done = QPushButton("Bruk") btn_done.clicked.connect(lambda: self.update_columns_dict(grid)) btn_done.setFixedHeight(50) # Layout main vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addSpacing(40) vbox.addWidget(btn_done) self.setLayout(vbox) def update_columns_dict(self, grid): """Update the table structure dict. Based on which checkboxes that are checked and unchecked the dict that holds the table structure (self.columns) gets updated. Parameters ---------- grid : QGridLayout The grid holding the checkboxes and headers. """ for column_index in range(grid.columnCount()): main_header = grid.itemAtPosition(0, column_index).widget().text() checkboxes = grid.itemAtPosition(1, column_index) for checkbox_index in range(checkboxes.count()): checkbox = checkboxes.itemAt(checkbox_index).widget() sub_header = checkbox.text() self.columns[main_header][sub_header] = checkbox.isChecked() self.accept() def get_columns_dict(self): """Getter for the frequency table structure dict. Returns ------- self.columns : dict The frequency table structure dict. """ return self.columns
Ancestors
- PySide2.QtWidgets.QDialog
- PySide2.QtWidgets.QWidget
- PySide2.QtCore.QObject
- PySide2.QtGui.QPaintDevice
- Shiboken.Object
Class variables
var staticMetaObject
Methods
def get_columns_dict(self)
-
Getter for the frequency table structure dict.
Returns
self.columns
:dict
- The frequency table structure dict.
Expand source code
def get_columns_dict(self): """Getter for the frequency table structure dict. Returns ------- self.columns : dict The frequency table structure dict. """ return self.columns
def update_columns_dict(self, grid)
-
Update the table structure dict.
Based on which checkboxes that are checked and unchecked the dict that holds the table structure (self.columns) gets updated.
Parameters
grid
:QGridLayout
- The grid holding the checkboxes and headers.
Expand source code
def update_columns_dict(self, grid): """Update the table structure dict. Based on which checkboxes that are checked and unchecked the dict that holds the table structure (self.columns) gets updated. Parameters ---------- grid : QGridLayout The grid holding the checkboxes and headers. """ for column_index in range(grid.columnCount()): main_header = grid.itemAtPosition(0, column_index).widget().text() checkboxes = grid.itemAtPosition(1, column_index) for checkbox_index in range(checkboxes.count()): checkbox = checkboxes.itemAt(checkbox_index).widget() sub_header = checkbox.text() self.columns[main_header][sub_header] = checkbox.isChecked() self.accept()
class FrequencyTableModule (data=None)
-
Table with overview of radio communication attributes.
A frequency table is an overview of information needed to establish radio communication.
This module includes:
Components
- Header
- Table with predefined columns
- Buttons for editing module
- Popup (ColumnsChoicePopup)
Features
- Keyboard shortcuts for editing module
- Buttons only visible when mouse in modules space
- Interface generated from configuration file
- Automatic resizing
- Able to be loaded from data parameter
Parameters
data
:dict
- Module content, used if not None. See self.get_data().
Class-variable 'type' should be set by derived class.
Expand source code
class FrequencyTableModule(ModuleBase, QWidget, metaclass=Meta): """Table with overview of radio communication attributes. A frequency table is an overview of information needed to establish radio communication. # This module includes: ## Components * Header * Table with predefined columns * Buttons for editing module * Popup (ColumnsChoicePopup) ## Features * Keyboard shortcuts for editing module * Buttons only visible when mouse in modules space * Interface generated from configuration file * Automatic resizing * Able to be loaded from data parameter Parameters ---------- data : dict Module content, used if not None. See self.get_data(). """ def __init__(self, data=None): self.type = FrequencyTableModule.__name__ QWidget.__init__(self) ModuleBase.__init__(self) with open( "soitool/modules/config/module_frequency_table.json", "r", encoding="utf-8", ) as config_file: self.columns = load(config_file) # Header self.header = QLabel("FREKVENSTABELL") self.header.setFont(HEADLINE_FONT) self.header.setFixedSize(322, 40) # Table self.table = self.__create_table() self.table.cellChanged.connect(self.resize) # Buttons self.buttons = self.__create_buttons() # Layout layout = QVBoxLayout() layout.setMargin(0) layout.setSpacing(0) layout.setAlignment(Qt.AlignTop | Qt.AlignLeft) layout.addWidget(self.header, alignment=Qt.AlignCenter) layout.addWidget(self.table) layout.addWidget(self.buttons) self.setLayout(layout) if not data: self.set_columns() else: self.load_data_to_module(data) # !!!!! MAIN COMPONENTS SETUP !!!!! def __create_table(self): """Create the frequency table itself. Returns ------- table : QTableWidget The frequency table itself. """ table = QTableWidget(3, self.get_max_number_of_columns()) table.setFont(DEFAULT_FONT) table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) table.verticalHeader().hide() table.horizontalHeader().hide() self.setStyleSheet("QTableView { gridline-color: black; }") column = 0 # Set main headers for main_header in self.columns.keys(): span = len(self.columns[main_header]) if span > 1: table.setSpan(0, column, 1, span) item = QTableWidgetItem(main_header) item.setFlags(item.flags() ^ Qt.ItemIsEditable) item.setFont(HEADLINE_FONT) table.setItem(0, column, item) # Set subheaders for sub_header in self.columns[main_header].keys(): item = QTableWidgetItem(sub_header) item.setFlags(item.flags() ^ Qt.ItemIsEditable) item.setFont(SUB_HEADLINE_FONT) table.setItem(1, column, item) column += 1 return table def __create_buttons(self): """Create the buttons for editing table. Returns ------- buttons_wrapper : QWidget Wrapper widget containing the buttons to edit table. """ # Button for selecting columns btn_columns = QPushButton("Kolonner") btn_columns.setFixedWidth(100) btn_columns.clicked.connect(self.open_columns_popup) # Button for adding row btn_add = QPushButton(" + ") btn_add.setFixedWidth(50) btn_add.clicked.connect(self.add_row) # Buttons for removing row btn_remove = QPushButton(" - ") btn_remove.setFixedWidth(50) btn_remove.clicked.connect(self.remove_row) # Layout hbox = QHBoxLayout() hbox.setSpacing(0) hbox.setMargin(0) hbox.addWidget(btn_columns) hbox.addWidget(btn_add) hbox.addWidget(btn_remove) # Wrapping layout in a widget buttons_wrapper = QWidget() buttons_wrapper.setFixedWidth(FIXED_WIDTH) buttons_wrapper.setFixedHeight(33) buttons_wrapper.setLayout(hbox) buttons_wrapper.hide() return buttons_wrapper # !!!!! EVENT HANDLERS !!!!! def enterEvent(self, event): """Eventhandler for showing buttons when mouse enters widget. Parameters ---------- event : enterEvent Called when mouse enter widgets screen space. """ self.buttons.show() self.resize() QWidget.enterEvent(self, event) def leaveEvent(self, event): """Eventhandler for hiding buttons when mouse leaves widget. Parameters ---------- event : eneterEvent Called when mouse leaves widgets screen space. """ self.buttons.hide() self.resize() QWidget.leaveEvent(self, event) def keyPressEvent(self, event): """Eventhandler for adding and removing rows and columns. Parameters ---------- event : keyPressEvent Holds which keys were pressed. """ if is_event_remove_row(event): self.remove_row() elif is_event_edit_module(event): self.open_columns_popup() elif is_event_add_row(event): self.add_row() else: super().keyPressEvent(event) # !!!!! TABLE OPERATIONS !!!!! def get_max_number_of_columns(self): """Get number of maximum number of columns in table. Returns ------- number : int The max number of columns. """ number = 0 for value in self.columns.values(): number += len(value) return number def add_row(self): """Add column to table.""" if self.table.currentRow() > 0: self.table.insertRow(self.table.currentRow() + 1) else: self.table.setRowCount(self.table.rowCount() + 1) self.resize() def remove_row(self): """Remove a column and protect header rows.""" if self.table.currentRow() > 1: self.table.removeRow(self.table.currentRow()) self.resize() def set_columns(self): """Set wich columns to hide and show based on self.columns dict.""" for main_header in self.columns.keys(): for sub_header in self.columns[main_header].keys(): self.table.setColumnHidden( self.get_column_index_by_headers(main_header, sub_header), not (self.columns[main_header][sub_header]), ) # To prevent table to get scrolled out of widget when adding columns self.table.horizontalScrollBar().setValue(0) self.resize() def get_column_index_by_headers(self, main_header, sub_header): """Find column index belonging to self.columns dict element. Parameters ---------- main_header : string First key in self.columns. sub_header : string Second key in self.columns. Returns ------- sub_column : int Index of column. Raises ------ LookupError If column not found. """ for main_column in range(self.table.columnCount()): item = self.table.item(0, main_column) if item is not None and item.text() == main_header: span = self.table.columnSpan(0, main_column) for sub_column in range(main_column, main_column + span): if self.table.item(1, sub_column).text() == sub_header: return sub_column raise LookupError( "self.colums[" + main_header + "][" + sub_header + "]" ) # !!!!! MODULE METHODS !!!!! def resize(self): """Resize module to content.""" resize_table(self.table) width = max(self.header.minimumWidth(), self.table.minimumWidth()) if self.buttons.isVisible() and self.buttons.minimumWidth() > width: width = self.buttons.minimumWidth() self.setFixedWidth(width) height = self.header.minimumHeight() + self.table.minimumHeight() if self.buttons.isVisible(): height += self.buttons.minimumHeight() self.setFixedHeight(height) def open_columns_popup(self): """Open column edit dialog.""" popup = ColumnsChoicePopup(self.columns) popup.exec_() self.columns = popup.get_columns_dict() self.set_columns() def load_data_to_module(self, data): """Load module content from data. Parameters ---------- data : dict Module serialized as a dict. format = { main_header: { sub_header1: [row1, row2, row3], sub_header2: [row1, row2, row3], }, main_header2: {...}, } """ # Updating wich columns to hide and show for main_header in self.columns.keys(): if main_header in data.keys(): for sub_header in self.columns[main_header].keys(): if sub_header in data[main_header].keys(): self.columns[main_header][sub_header] = True else: self.columns[main_header][sub_header] = False else: for sub_header in self.columns[main_header].keys(): self.columns[main_header][sub_header] = False # Updating column structure according to self.columns self.set_columns() # Needed rows number_of_rows = len(list(data[list(data.keys())[0]].values())[0]) # -1 because one row is already there self.table.setRowCount(self.table.rowCount() + number_of_rows - 1) # Adding content to table for main_header in self.columns.keys(): for sub_header in self.columns[main_header].keys(): if self.columns[main_header][sub_header]: for row_index in range(2, number_of_rows + 2): column_index = self.get_column_index_by_headers( main_header, sub_header ) current_item = QTableWidgetItem( data[main_header][sub_header][row_index - 2] ) self.table.setItem( row_index, column_index, current_item ) # !!!!! MODULE BASE METHODS !!!!! def get_size(self): """Get module dimensions. Returns ------- tuple (width, heigth) """ self.resize() return (self.minimumWidth(), self.minimumHeight()) def get_data(self): """Serialize the module data. Returns ------- data : dict Table structure and data in a dict Format: { main_header1 : { sub_header1 : [row1, row2, row3], sub_header2 : [row1, row2, row3] }, main_header2 : { sub_header3 : [row1, row2, row3], ... }, ... } """ data = {} column_index = 0 # Loop trough every main header while column_index < self.table.columnCount(): span = self.table.columnSpan(0, column_index) main_header = self.table.item(0, column_index).text() sub_data = {} # Loop trough every sub header under current main header for sub_column_index in range(column_index, column_index + span): if not self.table.isColumnHidden(sub_column_index): sub_header = self.table.item(1, sub_column_index).text() columns_data = [] # Loop through every row under current sub header for row_index in range(2, self.table.rowCount()): item = self.table.item(row_index, sub_column_index) if item is None: columns_data.append("") else: columns_data.append(item.text()) sub_data[sub_header] = columns_data if sub_data: data[main_header] = sub_data column_index += span return data @staticmethod def get_user_friendly_name(): """Get user friendly name of module. Returns ------- string User friendly name. """ return "Frekvenstabell" def prepare_for_pdf_export(self): """Prepare for PDF-export.""" prepare_table_for_pdf_export(self.table) @staticmethod def get_icon(): """Get module icon. Returns ------- QIcon Icon of the module. """ return QIcon("soitool/media/frequencytable.png")
Ancestors
- ModuleBase
- abc.ABC
- PySide2.QtWidgets.QWidget
- PySide2.QtCore.QObject
- PySide2.QtGui.QPaintDevice
- Shiboken.Object
Class variables
var staticMetaObject
Static methods
def get_icon()
-
Get module icon.
Returns
QIcon
- Icon of the module.
Expand source code
@staticmethod def get_icon(): """Get module icon. Returns ------- QIcon Icon of the module. """ return QIcon("soitool/media/frequencytable.png")
def get_user_friendly_name()
-
Get user friendly name of module.
Returns
string
- User friendly name.
Expand source code
@staticmethod def get_user_friendly_name(): """Get user friendly name of module. Returns ------- string User friendly name. """ return "Frekvenstabell"
Methods
def add_row(self)
-
Add column to table.
Expand source code
def add_row(self): """Add column to table.""" if self.table.currentRow() > 0: self.table.insertRow(self.table.currentRow() + 1) else: self.table.setRowCount(self.table.rowCount() + 1) self.resize()
def enterEvent(self, event)
-
Eventhandler for showing buttons when mouse enters widget.
Parameters
event
:enterEvent
- Called when mouse enter widgets screen space.
Expand source code
def enterEvent(self, event): """Eventhandler for showing buttons when mouse enters widget. Parameters ---------- event : enterEvent Called when mouse enter widgets screen space. """ self.buttons.show() self.resize() QWidget.enterEvent(self, event)
def get_column_index_by_headers(self, main_header, sub_header)
-
Find column index belonging to self.columns dict element.
Parameters
main_header
:string
- First key in self.columns.
sub_header
:string
- Second key in self.columns.
Returns
sub_column
:int
- Index of column.
Raises
LookupError
- If column not found.
Expand source code
def get_column_index_by_headers(self, main_header, sub_header): """Find column index belonging to self.columns dict element. Parameters ---------- main_header : string First key in self.columns. sub_header : string Second key in self.columns. Returns ------- sub_column : int Index of column. Raises ------ LookupError If column not found. """ for main_column in range(self.table.columnCount()): item = self.table.item(0, main_column) if item is not None and item.text() == main_header: span = self.table.columnSpan(0, main_column) for sub_column in range(main_column, main_column + span): if self.table.item(1, sub_column).text() == sub_header: return sub_column raise LookupError( "self.colums[" + main_header + "][" + sub_header + "]" )
def get_data(self)
-
Serialize the module data.
Returns
data
:dict
-
Table structure and data in a dict
Format:
{ main_header1 : { sub_header1 : [row1, row2, row3], sub_header2 : [row1, row2, row3] }, main_header2 : { sub_header3 : [row1, row2, row3], … }, … }
Expand source code
def get_data(self): """Serialize the module data. Returns ------- data : dict Table structure and data in a dict Format: { main_header1 : { sub_header1 : [row1, row2, row3], sub_header2 : [row1, row2, row3] }, main_header2 : { sub_header3 : [row1, row2, row3], ... }, ... } """ data = {} column_index = 0 # Loop trough every main header while column_index < self.table.columnCount(): span = self.table.columnSpan(0, column_index) main_header = self.table.item(0, column_index).text() sub_data = {} # Loop trough every sub header under current main header for sub_column_index in range(column_index, column_index + span): if not self.table.isColumnHidden(sub_column_index): sub_header = self.table.item(1, sub_column_index).text() columns_data = [] # Loop through every row under current sub header for row_index in range(2, self.table.rowCount()): item = self.table.item(row_index, sub_column_index) if item is None: columns_data.append("") else: columns_data.append(item.text()) sub_data[sub_header] = columns_data if sub_data: data[main_header] = sub_data column_index += span return data
def get_max_number_of_columns(self)
-
Get number of maximum number of columns in table.
Returns
number
:int
- The max number of columns.
Expand source code
def get_max_number_of_columns(self): """Get number of maximum number of columns in table. Returns ------- number : int The max number of columns. """ number = 0 for value in self.columns.values(): number += len(value) return number
def get_size(self)
-
Get module dimensions.
Returns
tuple
- (width, heigth)
Expand source code
def get_size(self): """Get module dimensions. Returns ------- tuple (width, heigth) """ self.resize() return (self.minimumWidth(), self.minimumHeight())
def keyPressEvent(self, event)
-
Eventhandler for adding and removing rows and columns.
Parameters
event
:keyPressEvent
- Holds which keys were pressed.
Expand source code
def keyPressEvent(self, event): """Eventhandler for adding and removing rows and columns. Parameters ---------- event : keyPressEvent Holds which keys were pressed. """ if is_event_remove_row(event): self.remove_row() elif is_event_edit_module(event): self.open_columns_popup() elif is_event_add_row(event): self.add_row() else: super().keyPressEvent(event)
def leaveEvent(self, event)
-
Eventhandler for hiding buttons when mouse leaves widget.
Parameters
event
:eneterEvent
- Called when mouse leaves widgets screen space.
Expand source code
def leaveEvent(self, event): """Eventhandler for hiding buttons when mouse leaves widget. Parameters ---------- event : eneterEvent Called when mouse leaves widgets screen space. """ self.buttons.hide() self.resize() QWidget.leaveEvent(self, event)
def load_data_to_module(self, data)
-
Load module content from data.
Parameters
data
:dict
-
Module serialized as a dict.
format = { main_header: { sub_header1: [row1, row2, row3], sub_header2: [row1, row2, row3], }, main_header2: {…}, }
Expand source code
def load_data_to_module(self, data): """Load module content from data. Parameters ---------- data : dict Module serialized as a dict. format = { main_header: { sub_header1: [row1, row2, row3], sub_header2: [row1, row2, row3], }, main_header2: {...}, } """ # Updating wich columns to hide and show for main_header in self.columns.keys(): if main_header in data.keys(): for sub_header in self.columns[main_header].keys(): if sub_header in data[main_header].keys(): self.columns[main_header][sub_header] = True else: self.columns[main_header][sub_header] = False else: for sub_header in self.columns[main_header].keys(): self.columns[main_header][sub_header] = False # Updating column structure according to self.columns self.set_columns() # Needed rows number_of_rows = len(list(data[list(data.keys())[0]].values())[0]) # -1 because one row is already there self.table.setRowCount(self.table.rowCount() + number_of_rows - 1) # Adding content to table for main_header in self.columns.keys(): for sub_header in self.columns[main_header].keys(): if self.columns[main_header][sub_header]: for row_index in range(2, number_of_rows + 2): column_index = self.get_column_index_by_headers( main_header, sub_header ) current_item = QTableWidgetItem( data[main_header][sub_header][row_index - 2] ) self.table.setItem( row_index, column_index, current_item )
def open_columns_popup(self)
-
Open column edit dialog.
Expand source code
def open_columns_popup(self): """Open column edit dialog.""" popup = ColumnsChoicePopup(self.columns) popup.exec_() self.columns = popup.get_columns_dict() self.set_columns()
def prepare_for_pdf_export(self)
-
Prepare for PDF-export.
Expand source code
def prepare_for_pdf_export(self): """Prepare for PDF-export.""" prepare_table_for_pdf_export(self.table)
def remove_row(self)
-
Remove a column and protect header rows.
Expand source code
def remove_row(self): """Remove a column and protect header rows.""" if self.table.currentRow() > 1: self.table.removeRow(self.table.currentRow()) self.resize()
def resize(self)
-
Resize module to content.
Expand source code
def resize(self): """Resize module to content.""" resize_table(self.table) width = max(self.header.minimumWidth(), self.table.minimumWidth()) if self.buttons.isVisible() and self.buttons.minimumWidth() > width: width = self.buttons.minimumWidth() self.setFixedWidth(width) height = self.header.minimumHeight() + self.table.minimumHeight() if self.buttons.isVisible(): height += self.buttons.minimumHeight() self.setFixedHeight(height)
def set_columns(self)
-
Set wich columns to hide and show based on self.columns dict.
Expand source code
def set_columns(self): """Set wich columns to hide and show based on self.columns dict.""" for main_header in self.columns.keys(): for sub_header in self.columns[main_header].keys(): self.table.setColumnHidden( self.get_column_index_by_headers(main_header, sub_header), not (self.columns[main_header][sub_header]), ) # To prevent table to get scrolled out of widget when adding columns self.table.horizontalScrollBar().setValue(0) self.resize()
class Meta (name, bases, namespace, **kwargs)
-
Used as a metaclass to enable multiple inheritance.
Expand source code
class Meta(type(ModuleBase), type(QWidget)): """Used as a metaclass to enable multiple inheritance."""
Ancestors
- abc.ABCMeta
- Shiboken.ObjectType
- builtins.type