Module soitool.modules.module_phonebook

SOI module for functions and associated contact informations.

Expand source code
"""SOI module for functions and associated contact informations."""

from json import load
from PySide2.QtWidgets import (
    QWidget,
    QDialog,
    QLabel,
    QVBoxLayout,
    QTableWidget,
    QPushButton,
    QHBoxLayout,
    QTableWidgetItem,
    QCheckBox,
)
from PySide2.QtCore import Qt, QSize
from PySide2.QtGui import QBrush, QColor, QIcon
from soitool.modules.module_base import (
    ModuleBase,
    DEFAULT_FONT,
    SUB_HEADLINE_FONT,
    resize_table,
    get_table_size,
    prepare_table_for_pdf_export,
    is_event_add_row,
    is_event_remove_row,
    is_event_edit_module,
)


class ColumnsChoicePopup(QDialog):
    """A popup for selecting wich columns to hide/show in phonebook.

    Parameters
    ----------
    QDialog : QDialog
        Parent class for popup functionality.
    selected_columns : dict
        Table structure for columns to show/hide.
    """

    def __init__(self, selected_columns):
        super().__init__()
        self.selected_columns = selected_columns
        self.setWindowTitle("Kolonner")
        self.setMinimumWidth(115)
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)

        # Layout
        layout = QVBoxLayout()

        # Checkboxes
        for header in self.selected_columns.keys():
            box = QCheckBox(header)
            box.setChecked(self.selected_columns[header])
            layout.addWidget(box)

        # Button
        btn_done = QPushButton("Bruk")
        btn_done.clicked.connect(lambda: self.update_selected_columns(layout))
        layout.addWidget(btn_done)

        self.setLayout(layout)

    def update_selected_columns(self, layout):
        """Update the dict for columns to hide/show based on checkboxes.

        Parameters
        ----------
        layout : QLayout
            Layout with checkboxes to loop trough.
        """
        for item_index in range(layout.count()):
            item = layout.itemAt(item_index).widget()
            if isinstance(item, QCheckBox):
                self.selected_columns[item.text()] = item.isChecked()
        self.accept()

    def get_selected_columns(self):
        """Getter for the table structure.

        Returns
        -------
        dict
            The table structure.
        """
        return self.selected_columns


class Meta(type(ModuleBase), type(QWidget)):
    """Used as a metaclass to enable multiple inheritance."""


class PhonebookModule(ModuleBase, QWidget, metaclass=Meta):
    """SOI module for functions and associated contact informations.

    # This module includes:

    ## Components

    * Header
    * Table with predefined columns
    * Buttons for editing module
    * Popup (ColumnsChoicePopup)

    ## Features

    * Keybord shortcuts for editing module
    * Buttons only visble when mouse in modules space
    * Interface generated from config file
    * Popup for editing columns
    * 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 = PhonebookModule.__name__
        QWidget.__init__(self)
        ModuleBase.__init__(self)

        # Table structure
        with open(
            "soitool/modules/config/module_phonebook.json",
            "r",
            encoding="utf-8",
        ) as config_file:
            self.selected_columns = load(config_file)

        # Header
        self.header = QLabel("TELEFONLISTE")
        self.header.setFont(self.headline_font)
        self.header.setFixedSize(QSize(265, 40))

        # Table
        self.table = self.__create_table()
        self.table.cellChanged.connect(self.resize)
        # To ensure table is initially larger than title
        self.table.horizontalHeader().setMinimumSectionSize(135)

        # Buttons
        self.buttons = self.__creat_buttons()

        # Layout
        layout = QVBoxLayout()
        layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.header)
        layout.setAlignment(self.header, Qt.AlignCenter)
        layout.addWidget(self.table)
        layout.addWidget(self.buttons)
        self.setLayout(layout)

        if data:
            self.load_data_to_module(data)
        else:
            self.set_columns()

    #   !!!!! CREATE MAIN COMPONENTS !!!!!!

    def __create_table(self):
        """Add phonebook table.

        Returns
        -------
        QTableWidget
            The phonebook table.
        """
        table = QTableWidget(2, len(self.selected_columns))
        table.setFont(DEFAULT_FONT)
        table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        table.horizontalHeader().hide()
        table.verticalHeader().hide()
        table.setStyleSheet("QTableView { gridline-color: black; }")

        # Headers - setup
        column_index = 0
        for header in self.selected_columns:
            header_item = QTableWidgetItem(header)
            header_item.setFont(SUB_HEADLINE_FONT)
            header_item.setFlags(header_item.flags() ^ Qt.ItemIsEditable)
            header_item.setBackground(QBrush(QColor("black")))
            header_item.setForeground(QBrush(QColor("white")))
            table.setItem(0, column_index, header_item)
            column_index += 1

        return table

    def __creat_buttons(self):
        """Add buttons for editing phonebook table.

        Returns
        -------
        QWidget
            Widget holding a layout with buttons.
        """
        # Button for editing columns
        btn_components = QPushButton("Kolonner", self)
        btn_components.clicked.connect(self.open_popup)
        btn_components.setFixedWidth(100)

        # Buttons for adding row
        btn_add = QPushButton(" + ", self)
        btn_add.clicked.connect(self.add_row)
        btn_add.setFixedWidth(50)

        # Buttons for removing row
        btn_remove = QPushButton(" - ", self)
        btn_remove.clicked.connect(self.remove_row)
        btn_remove.setFixedWidth(50)

        # Layout for structure
        hbox = QHBoxLayout()
        hbox.setSpacing(0)
        hbox.setSpacing(0)
        hbox.addWidget(btn_components)
        hbox.addWidget(btn_add)
        hbox.addWidget(btn_remove)

        # Widget wrapping buttons
        wrapper = QWidget()
        wrapper.setFixedSize(QSize(230, 50))
        wrapper.setLayout(hbox)
        wrapper.hide()

        return wrapper

    #   !!!!! EVENT HADNDLERS !!!!!

    def enterEvent(self, event):
        """Eventhandler for showing buttons when mous enters widgets space.

        Parameters
        ----------
        event : enterEvent
            Called when mouse enters widgets space.
        """
        self.buttons.show()
        self.resize()
        QTableWidget.enterEvent(self, event)

    def leaveEvent(self, event):
        """Eventhandler for hiding buttons when mouse leaves widgets space.

        Parameters
        ----------
        event : enterEvent
            Called when mosue leaves widgets space.
        """
        self.buttons.hide()
        self.resize()
        QTableWidget.enterEvent(self, event)

    def keyPressEvent(self, event):
        """Keyboard shortcuts for adding/removing rows and selecting columns.

        Parameters
        ----------
        event : keyPressEvent
            Called when keys are pressed.
        """
        if is_event_add_row(event):
            self.add_row()
        elif is_event_remove_row(event):
            self.remove_row()
        elif is_event_edit_module(event):
            self.open_popup()
        else:
            super().keyPressEvent(event)

    #   !!!!! TABLE OPERATIONS !!!!!

    def add_row(self):
        """Add row to phonebook table."""
        self.table.insertRow(self.table.currentRow() + 1)
        self.resize()

    def remove_row(self):
        """Remove selected row from phonebook table."""
        if self.table.currentRow() > 0:
            self.table.removeRow(self.table.currentRow())
            self.resize()

    def set_columns(self):
        """Update table columns visibility based on selected_columns."""
        for header in self.selected_columns.keys():
            self.table.setColumnHidden(
                self.get_column_index_by_header(header),
                not (self.selected_columns[header]),
            )
        self.resize()
        # setColumnHidden makes the next added row appear above the title, so
        # forcing a valid cell here
        self.table.setCurrentCell(1, 0)

    def get_column_index_by_header(self, header):
        """Get index for column containing a spesific header.

        Parameters
        ----------
        header : string
            The header to search for.

        Returns
        -------
        int
            The column index for where the header was found.

        Raises
        ------
        LookupError
            Unable to find header in table headers.
        """
        for column_index in range(self.table.columnCount()):
            self.table.setCurrentCell(0, column_index)
            if self.table.currentItem().text() == header:
                return column_index

        raise LookupError("'" + header + "' not in table headers.")

    #   !!!!! MODULE OPERATIONs !!!!!

    def resize(self):
        """Resize whole module based on content."""
        resize_table(self.table)

        width = max(get_table_size(self.table)[0], self.header.minimumWidth())
        if self.buttons.isVisible():
            width = max(width, self.buttons.minimumWidth())

        self.setFixedWidth(width)

        height = get_table_size(self.table)[1] + self.header.minimumHeight()
        if self.buttons.isVisible():
            height += self.buttons.minimumHeight()

        self.setFixedHeight(height)

    def load_data_to_module(self, data):
        """Load module content from data.

        Parameters
        ----------
        data : dict
            Module serialized as dict.
        """
        for column_header in self.selected_columns.keys():
            if column_header in data.keys():
                self.selected_columns[column_header] = True
            else:
                self.selected_columns[column_header] = False

        self.set_columns()

        number_of_rows = len(list(data.values())[0])
        self.table.setRowCount(self.table.rowCount() + number_of_rows - 1)

        for column_header in data.keys():
            column_index = self.get_column_index_by_header(column_header)
            for row_index in range(1, number_of_rows + 1):
                current_item = QTableWidgetItem(
                    data[column_header][row_index - 1]
                )
                self.table.setItem(row_index, column_index, current_item)

        # Select first cell
        self.table.setCurrentCell(1, 0)

    def open_popup(self):
        """Open dialog for editing columns."""
        popup = ColumnsChoicePopup(self.selected_columns)
        popup.exec_()
        self.selected_columns = popup.get_selected_columns()
        self.set_columns()

    #   !!!!! MODULE BASE OPERATIONS !!!!!

    def get_size(self):
        """Getter for module size.

        Returns
        -------
        tuple
            Size of the module (width, height)
        """
        self.resize()
        return (self.minimumWidth(), self.minimumHeight())

    def get_data(self):
        """Get module content as serialized data.

        Returns
        -------
        dict
            Serialized module content.

            Format: {
                "header1" : ["row1", "row2", "row3"],
                "header2": ["row1", ..]
            }
        """
        data = {}

        for column_index in range(self.table.columnCount()):
            header = self.table.item(0, column_index).text()
            if self.selected_columns[header]:
                row_data = []
                for row_index in range(1, self.table.rowCount()):
                    row_item = self.table.item(row_index, column_index)
                    if row_item:
                        row_data.append(row_item.text())
                    else:
                        row_data.append("")
                data[header] = row_data

        return data

    def prepare_for_pdf_export(self):
        """Prepare for PDF-export."""
        prepare_table_for_pdf_export(self.table)

    @staticmethod
    def get_user_friendly_name():
        """Get user-friendly name of module.

        Returns
        -------
        string
            User-friendly name.
        """
        return "Telefonliste"

    @staticmethod
    def get_icon():
        """Get icon.

        Returns
        -------
        QIcon
            Picture of module.
        """
        return QIcon("soitool/media/phonebook.png")

Classes

class ColumnsChoicePopup (selected_columns)

A popup for selecting wich columns to hide/show in phonebook.

Parameters

QDialog : QDialog
Parent class for popup functionality.
selected_columns : dict
Table structure for columns to show/hide.
Expand source code
class ColumnsChoicePopup(QDialog):
    """A popup for selecting wich columns to hide/show in phonebook.

    Parameters
    ----------
    QDialog : QDialog
        Parent class for popup functionality.
    selected_columns : dict
        Table structure for columns to show/hide.
    """

    def __init__(self, selected_columns):
        super().__init__()
        self.selected_columns = selected_columns
        self.setWindowTitle("Kolonner")
        self.setMinimumWidth(115)
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)

        # Layout
        layout = QVBoxLayout()

        # Checkboxes
        for header in self.selected_columns.keys():
            box = QCheckBox(header)
            box.setChecked(self.selected_columns[header])
            layout.addWidget(box)

        # Button
        btn_done = QPushButton("Bruk")
        btn_done.clicked.connect(lambda: self.update_selected_columns(layout))
        layout.addWidget(btn_done)

        self.setLayout(layout)

    def update_selected_columns(self, layout):
        """Update the dict for columns to hide/show based on checkboxes.

        Parameters
        ----------
        layout : QLayout
            Layout with checkboxes to loop trough.
        """
        for item_index in range(layout.count()):
            item = layout.itemAt(item_index).widget()
            if isinstance(item, QCheckBox):
                self.selected_columns[item.text()] = item.isChecked()
        self.accept()

    def get_selected_columns(self):
        """Getter for the table structure.

        Returns
        -------
        dict
            The table structure.
        """
        return self.selected_columns

Ancestors

  • PySide2.QtWidgets.QDialog
  • PySide2.QtWidgets.QWidget
  • PySide2.QtCore.QObject
  • PySide2.QtGui.QPaintDevice
  • Shiboken.Object

Class variables

var staticMetaObject

Methods

def get_selected_columns(self)

Getter for the table structure.

Returns

dict
The table structure.
Expand source code
def get_selected_columns(self):
    """Getter for the table structure.

    Returns
    -------
    dict
        The table structure.
    """
    return self.selected_columns
def update_selected_columns(self, layout)

Update the dict for columns to hide/show based on checkboxes.

Parameters

layout : QLayout
Layout with checkboxes to loop trough.
Expand source code
def update_selected_columns(self, layout):
    """Update the dict for columns to hide/show based on checkboxes.

    Parameters
    ----------
    layout : QLayout
        Layout with checkboxes to loop trough.
    """
    for item_index in range(layout.count()):
        item = layout.itemAt(item_index).widget()
        if isinstance(item, QCheckBox):
            self.selected_columns[item.text()] = item.isChecked()
    self.accept()
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
class PhonebookModule (data=None)

SOI module for functions and associated contact informations.

This module includes:

Components

  • Header
  • Table with predefined columns
  • Buttons for editing module
  • Popup (ColumnsChoicePopup)

Features

  • Keybord shortcuts for editing module
  • Buttons only visble when mouse in modules space
  • Interface generated from config file
  • Popup for editing columns
  • 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 PhonebookModule(ModuleBase, QWidget, metaclass=Meta):
    """SOI module for functions and associated contact informations.

    # This module includes:

    ## Components

    * Header
    * Table with predefined columns
    * Buttons for editing module
    * Popup (ColumnsChoicePopup)

    ## Features

    * Keybord shortcuts for editing module
    * Buttons only visble when mouse in modules space
    * Interface generated from config file
    * Popup for editing columns
    * 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 = PhonebookModule.__name__
        QWidget.__init__(self)
        ModuleBase.__init__(self)

        # Table structure
        with open(
            "soitool/modules/config/module_phonebook.json",
            "r",
            encoding="utf-8",
        ) as config_file:
            self.selected_columns = load(config_file)

        # Header
        self.header = QLabel("TELEFONLISTE")
        self.header.setFont(self.headline_font)
        self.header.setFixedSize(QSize(265, 40))

        # Table
        self.table = self.__create_table()
        self.table.cellChanged.connect(self.resize)
        # To ensure table is initially larger than title
        self.table.horizontalHeader().setMinimumSectionSize(135)

        # Buttons
        self.buttons = self.__creat_buttons()

        # Layout
        layout = QVBoxLayout()
        layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.header)
        layout.setAlignment(self.header, Qt.AlignCenter)
        layout.addWidget(self.table)
        layout.addWidget(self.buttons)
        self.setLayout(layout)

        if data:
            self.load_data_to_module(data)
        else:
            self.set_columns()

    #   !!!!! CREATE MAIN COMPONENTS !!!!!!

    def __create_table(self):
        """Add phonebook table.

        Returns
        -------
        QTableWidget
            The phonebook table.
        """
        table = QTableWidget(2, len(self.selected_columns))
        table.setFont(DEFAULT_FONT)
        table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        table.horizontalHeader().hide()
        table.verticalHeader().hide()
        table.setStyleSheet("QTableView { gridline-color: black; }")

        # Headers - setup
        column_index = 0
        for header in self.selected_columns:
            header_item = QTableWidgetItem(header)
            header_item.setFont(SUB_HEADLINE_FONT)
            header_item.setFlags(header_item.flags() ^ Qt.ItemIsEditable)
            header_item.setBackground(QBrush(QColor("black")))
            header_item.setForeground(QBrush(QColor("white")))
            table.setItem(0, column_index, header_item)
            column_index += 1

        return table

    def __creat_buttons(self):
        """Add buttons for editing phonebook table.

        Returns
        -------
        QWidget
            Widget holding a layout with buttons.
        """
        # Button for editing columns
        btn_components = QPushButton("Kolonner", self)
        btn_components.clicked.connect(self.open_popup)
        btn_components.setFixedWidth(100)

        # Buttons for adding row
        btn_add = QPushButton(" + ", self)
        btn_add.clicked.connect(self.add_row)
        btn_add.setFixedWidth(50)

        # Buttons for removing row
        btn_remove = QPushButton(" - ", self)
        btn_remove.clicked.connect(self.remove_row)
        btn_remove.setFixedWidth(50)

        # Layout for structure
        hbox = QHBoxLayout()
        hbox.setSpacing(0)
        hbox.setSpacing(0)
        hbox.addWidget(btn_components)
        hbox.addWidget(btn_add)
        hbox.addWidget(btn_remove)

        # Widget wrapping buttons
        wrapper = QWidget()
        wrapper.setFixedSize(QSize(230, 50))
        wrapper.setLayout(hbox)
        wrapper.hide()

        return wrapper

    #   !!!!! EVENT HADNDLERS !!!!!

    def enterEvent(self, event):
        """Eventhandler for showing buttons when mous enters widgets space.

        Parameters
        ----------
        event : enterEvent
            Called when mouse enters widgets space.
        """
        self.buttons.show()
        self.resize()
        QTableWidget.enterEvent(self, event)

    def leaveEvent(self, event):
        """Eventhandler for hiding buttons when mouse leaves widgets space.

        Parameters
        ----------
        event : enterEvent
            Called when mosue leaves widgets space.
        """
        self.buttons.hide()
        self.resize()
        QTableWidget.enterEvent(self, event)

    def keyPressEvent(self, event):
        """Keyboard shortcuts for adding/removing rows and selecting columns.

        Parameters
        ----------
        event : keyPressEvent
            Called when keys are pressed.
        """
        if is_event_add_row(event):
            self.add_row()
        elif is_event_remove_row(event):
            self.remove_row()
        elif is_event_edit_module(event):
            self.open_popup()
        else:
            super().keyPressEvent(event)

    #   !!!!! TABLE OPERATIONS !!!!!

    def add_row(self):
        """Add row to phonebook table."""
        self.table.insertRow(self.table.currentRow() + 1)
        self.resize()

    def remove_row(self):
        """Remove selected row from phonebook table."""
        if self.table.currentRow() > 0:
            self.table.removeRow(self.table.currentRow())
            self.resize()

    def set_columns(self):
        """Update table columns visibility based on selected_columns."""
        for header in self.selected_columns.keys():
            self.table.setColumnHidden(
                self.get_column_index_by_header(header),
                not (self.selected_columns[header]),
            )
        self.resize()
        # setColumnHidden makes the next added row appear above the title, so
        # forcing a valid cell here
        self.table.setCurrentCell(1, 0)

    def get_column_index_by_header(self, header):
        """Get index for column containing a spesific header.

        Parameters
        ----------
        header : string
            The header to search for.

        Returns
        -------
        int
            The column index for where the header was found.

        Raises
        ------
        LookupError
            Unable to find header in table headers.
        """
        for column_index in range(self.table.columnCount()):
            self.table.setCurrentCell(0, column_index)
            if self.table.currentItem().text() == header:
                return column_index

        raise LookupError("'" + header + "' not in table headers.")

    #   !!!!! MODULE OPERATIONs !!!!!

    def resize(self):
        """Resize whole module based on content."""
        resize_table(self.table)

        width = max(get_table_size(self.table)[0], self.header.minimumWidth())
        if self.buttons.isVisible():
            width = max(width, self.buttons.minimumWidth())

        self.setFixedWidth(width)

        height = get_table_size(self.table)[1] + self.header.minimumHeight()
        if self.buttons.isVisible():
            height += self.buttons.minimumHeight()

        self.setFixedHeight(height)

    def load_data_to_module(self, data):
        """Load module content from data.

        Parameters
        ----------
        data : dict
            Module serialized as dict.
        """
        for column_header in self.selected_columns.keys():
            if column_header in data.keys():
                self.selected_columns[column_header] = True
            else:
                self.selected_columns[column_header] = False

        self.set_columns()

        number_of_rows = len(list(data.values())[0])
        self.table.setRowCount(self.table.rowCount() + number_of_rows - 1)

        for column_header in data.keys():
            column_index = self.get_column_index_by_header(column_header)
            for row_index in range(1, number_of_rows + 1):
                current_item = QTableWidgetItem(
                    data[column_header][row_index - 1]
                )
                self.table.setItem(row_index, column_index, current_item)

        # Select first cell
        self.table.setCurrentCell(1, 0)

    def open_popup(self):
        """Open dialog for editing columns."""
        popup = ColumnsChoicePopup(self.selected_columns)
        popup.exec_()
        self.selected_columns = popup.get_selected_columns()
        self.set_columns()

    #   !!!!! MODULE BASE OPERATIONS !!!!!

    def get_size(self):
        """Getter for module size.

        Returns
        -------
        tuple
            Size of the module (width, height)
        """
        self.resize()
        return (self.minimumWidth(), self.minimumHeight())

    def get_data(self):
        """Get module content as serialized data.

        Returns
        -------
        dict
            Serialized module content.

            Format: {
                "header1" : ["row1", "row2", "row3"],
                "header2": ["row1", ..]
            }
        """
        data = {}

        for column_index in range(self.table.columnCount()):
            header = self.table.item(0, column_index).text()
            if self.selected_columns[header]:
                row_data = []
                for row_index in range(1, self.table.rowCount()):
                    row_item = self.table.item(row_index, column_index)
                    if row_item:
                        row_data.append(row_item.text())
                    else:
                        row_data.append("")
                data[header] = row_data

        return data

    def prepare_for_pdf_export(self):
        """Prepare for PDF-export."""
        prepare_table_for_pdf_export(self.table)

    @staticmethod
    def get_user_friendly_name():
        """Get user-friendly name of module.

        Returns
        -------
        string
            User-friendly name.
        """
        return "Telefonliste"

    @staticmethod
    def get_icon():
        """Get icon.

        Returns
        -------
        QIcon
            Picture of module.
        """
        return QIcon("soitool/media/phonebook.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 icon.

Returns

QIcon
Picture of module.
Expand source code
@staticmethod
def get_icon():
    """Get icon.

    Returns
    -------
    QIcon
        Picture of module.
    """
    return QIcon("soitool/media/phonebook.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 "Telefonliste"

Methods

def add_row(self)

Add row to phonebook table.

Expand source code
def add_row(self):
    """Add row to phonebook table."""
    self.table.insertRow(self.table.currentRow() + 1)
    self.resize()
def enterEvent(self, event)

Eventhandler for showing buttons when mous enters widgets space.

Parameters

event : enterEvent
Called when mouse enters widgets space.
Expand source code
def enterEvent(self, event):
    """Eventhandler for showing buttons when mous enters widgets space.

    Parameters
    ----------
    event : enterEvent
        Called when mouse enters widgets space.
    """
    self.buttons.show()
    self.resize()
    QTableWidget.enterEvent(self, event)
def get_column_index_by_header(self, header)

Get index for column containing a spesific header.

Parameters

header : string
The header to search for.

Returns

int
The column index for where the header was found.

Raises

LookupError
Unable to find header in table headers.
Expand source code
def get_column_index_by_header(self, header):
    """Get index for column containing a spesific header.

    Parameters
    ----------
    header : string
        The header to search for.

    Returns
    -------
    int
        The column index for where the header was found.

    Raises
    ------
    LookupError
        Unable to find header in table headers.
    """
    for column_index in range(self.table.columnCount()):
        self.table.setCurrentCell(0, column_index)
        if self.table.currentItem().text() == header:
            return column_index

    raise LookupError("'" + header + "' not in table headers.")
def get_data(self)

Get module content as serialized data.

Returns

dict

Serialized module content.

Format: { "header1" : ["row1", "row2", "row3"], "header2": ["row1", ..] }

Expand source code
def get_data(self):
    """Get module content as serialized data.

    Returns
    -------
    dict
        Serialized module content.

        Format: {
            "header1" : ["row1", "row2", "row3"],
            "header2": ["row1", ..]
        }
    """
    data = {}

    for column_index in range(self.table.columnCount()):
        header = self.table.item(0, column_index).text()
        if self.selected_columns[header]:
            row_data = []
            for row_index in range(1, self.table.rowCount()):
                row_item = self.table.item(row_index, column_index)
                if row_item:
                    row_data.append(row_item.text())
                else:
                    row_data.append("")
            data[header] = row_data

    return data
def get_size(self)

Getter for module size.

Returns

tuple
Size of the module (width, height)
Expand source code
def get_size(self):
    """Getter for module size.

    Returns
    -------
    tuple
        Size of the module (width, height)
    """
    self.resize()
    return (self.minimumWidth(), self.minimumHeight())
def keyPressEvent(self, event)

Keyboard shortcuts for adding/removing rows and selecting columns.

Parameters

event : keyPressEvent
Called when keys are pressed.
Expand source code
def keyPressEvent(self, event):
    """Keyboard shortcuts for adding/removing rows and selecting columns.

    Parameters
    ----------
    event : keyPressEvent
        Called when keys are pressed.
    """
    if is_event_add_row(event):
        self.add_row()
    elif is_event_remove_row(event):
        self.remove_row()
    elif is_event_edit_module(event):
        self.open_popup()
    else:
        super().keyPressEvent(event)
def leaveEvent(self, event)

Eventhandler for hiding buttons when mouse leaves widgets space.

Parameters

event : enterEvent
Called when mosue leaves widgets space.
Expand source code
def leaveEvent(self, event):
    """Eventhandler for hiding buttons when mouse leaves widgets space.

    Parameters
    ----------
    event : enterEvent
        Called when mosue leaves widgets space.
    """
    self.buttons.hide()
    self.resize()
    QTableWidget.enterEvent(self, event)
def load_data_to_module(self, data)

Load module content from data.

Parameters

data : dict
Module serialized as dict.
Expand source code
def load_data_to_module(self, data):
    """Load module content from data.

    Parameters
    ----------
    data : dict
        Module serialized as dict.
    """
    for column_header in self.selected_columns.keys():
        if column_header in data.keys():
            self.selected_columns[column_header] = True
        else:
            self.selected_columns[column_header] = False

    self.set_columns()

    number_of_rows = len(list(data.values())[0])
    self.table.setRowCount(self.table.rowCount() + number_of_rows - 1)

    for column_header in data.keys():
        column_index = self.get_column_index_by_header(column_header)
        for row_index in range(1, number_of_rows + 1):
            current_item = QTableWidgetItem(
                data[column_header][row_index - 1]
            )
            self.table.setItem(row_index, column_index, current_item)

    # Select first cell
    self.table.setCurrentCell(1, 0)
def open_popup(self)

Open dialog for editing columns.

Expand source code
def open_popup(self):
    """Open dialog for editing columns."""
    popup = ColumnsChoicePopup(self.selected_columns)
    popup.exec_()
    self.selected_columns = popup.get_selected_columns()
    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 selected row from phonebook table.

Expand source code
def remove_row(self):
    """Remove selected row from phonebook table."""
    if self.table.currentRow() > 0:
        self.table.removeRow(self.table.currentRow())
        self.resize()
def resize(self)

Resize whole module based on content.

Expand source code
def resize(self):
    """Resize whole module based on content."""
    resize_table(self.table)

    width = max(get_table_size(self.table)[0], self.header.minimumWidth())
    if self.buttons.isVisible():
        width = max(width, self.buttons.minimumWidth())

    self.setFixedWidth(width)

    height = get_table_size(self.table)[1] + self.header.minimumHeight()
    if self.buttons.isVisible():
        height += self.buttons.minimumHeight()

    self.setFixedHeight(height)
def set_columns(self)

Update table columns visibility based on selected_columns.

Expand source code
def set_columns(self):
    """Update table columns visibility based on selected_columns."""
    for header in self.selected_columns.keys():
        self.table.setColumnHidden(
            self.get_column_index_by_header(header),
            not (self.selected_columns[header]),
        )
    self.resize()
    # setColumnHidden makes the next added row appear above the title, so
    # forcing a valid cell here
    self.table.setCurrentCell(1, 0)