diff --git a/soitool/modules/module_code_phrase.py b/soitool/modules/module_code_phrase.py new file mode 100644 index 0000000000000000000000000000000000000000..c33ec30858601c14fd5a1339e5afccd31b6d303a --- /dev/null +++ b/soitool/modules/module_code_phrase.py @@ -0,0 +1,175 @@ +from PySide2.QtWidgets import QTableWidget, QTableWidgetItem +from PySide2 import QtGui, QtCore +from soitool.modules.module_base import ( + ModuleBase, + resize_table, + get_table_size, + HEADLINE_FONT, +) + +START_ROWS = 2 + + +class Meta(type(ModuleBase), type(QTableWidget)): + """Used as a metaclass to enable multiple inheritance.""" + + +# TODO write docstring +class CodePhraseModule(ModuleBase, QTableWidget, metaclass=Meta): + """Modified QTableWidget. + + By default, the widget initializes as an empty START_ROWS * START_COLUMNS + table. If parameters are given, the table initializes accordingly: + 'size' is a dict: {"width": int, "height": int}, + 'data' is a 2D list where data[x][y] represents row x, column y. + + The widget does not use more room than needed, and resizes dynamically. + Columnheaders are styled with light grey background and bold text. + It has shortcuts for adding and removing rows and columns. + + Inherits from ModuleBase and QTableWidget. + ModuleBase is used as an interface, it's methods are overridden. + """ + + def __init__(self, size=None, data=None): + self.type = "CodePhraseModule" + QTableWidget.__init__(self, 0, 2) + ModuleBase.__init__(self) + + # Remove headers and scrollbars + self.horizontalHeader().hide() + self.verticalHeader().hide() + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + if size is None and data is None: + for _ in range(START_ROWS): + self.add_row() + else: + # TODO check if copy-pasted block below can be used + self.setColumnCount(len(data[0])) + self.setRowCount(len(data)) + + # Set header-items + for i in range(self.columnCount()): + self.set_header_item(i, data[0][i]) + + # Set cell-items + for i in range(1, self.rowCount()): + for j in range(self.columnCount()): + item = QTableWidgetItem(data[i][j]) + self.setItem(i, j, item) + + self.resizeColumnsToContents() + self.resizeRowsToContents() + self.setFixedWidth(size["width"]) + self.setFixedHeight(size["height"]) + + self.cellChanged.connect(lambda: resize_table(self)) + + + # TODO update keybindings + # new row -> coded row with empty text + def keyPressEvent(self, event): + """Launch actions when specific combinations of keys are pressed. + + If the keys pressed are not related to a shortcut on this custom widget + the event is sent on to be handled by the superclass (for navigation + with arrow-keys for.eg.) + + Parameters + ---------- + event : QKeyEvent + event sent by Qt for us to handle + """ + if event.key() == QtCore.Qt.Key_Question: + self.add_column() + elif ( + event.modifiers() == QtCore.Qt.ShiftModifier + and event.key() == QtCore.Qt.Key_Underscore + ): + self.remove_column() + elif ( + event.modifiers() == QtCore.Qt.ControlModifier + and event.key() == QtCore.Qt.Key_Plus + ): + self.add_row() + elif ( + event.modifiers() == QtCore.Qt.ControlModifier + and event.key() == QtCore.Qt.Key_Underscore + ): + self.remove_row() + else: + super(CodePhraseModule, self).keyPressEvent(event) + + def set_header_item(self, column, text): + """Insert item with header-style. + + Item will always be set on header (top) row. + + Parameters + ---------- + column : int + What column index for inserting item. + text : String + What text the item should contain. + """ + item = QTableWidgetItem(text) + item.setBackground(QtGui.QBrush(QtGui.QColor(220, 220, 220))) + item.setFont(HEADLINE_FONT) + self.setItem(0, column, item) + + def add_row(self): + """Add row below selected row.""" + self.insertRow(self.currentRow() + 1) + item = QTableWidgetItem("koko") + self.setItem(self.currentRow() + 1,0, item) + resize_table(self) + + def remove_row(self): + """Remove selected row if two or more rows exist (including header).""" + if self.rowCount() > 2 and self.currentRow() != 0: + self.removeRow(self.currentRow()) + resize_table(self) + + def get_size(self): + """Return size of widget.""" + return get_table_size(self) + + def get_data(self): + """Return list containing module data. + + Returns + ------- + List (2D) + list[x][y] represents value of row x, column y. + """ + content = [] + for i in range(self.rowCount()): + row = [] + for j in range(self.columnCount()): + item = self.item(i, j) + if item is not None: + row.append(item.text()) + else: + row.append("") + content.append(row) + + return content + + @staticmethod + def get_user_friendly_name(): + """Get user-friendly name of module.""" + return "Generisk tabell" + + @staticmethod + def get_icon(): + """Get icon of module.""" + return QtGui.QIcon("soitool/media/tablemodule.png") + +if __name__ == '__main__': + from PySide2.QtWidgets import QApplication + app = QApplication() + w = CodePhraseModule() + w.show() + app.exec_()