diff --git a/README.md b/README.md
index 32f692b83e01dd3989fe97ac994d07a48f718a0a..5abde24ededc26891550f89aa9664f7ee9f19f30 100644
--- a/README.md
+++ b/README.md
@@ -29,13 +29,19 @@ deactivate
 
 ## Linting
 
-Sjekk av kodekvalitet gjøres med scriptet "CodeQualityCheck" i script-mappa, og er skrevet i Powershell(.ps1) og Bash(.sh). Scriptet kjører Pylint med .pylintrc-fil, Flake8, Bandit og pydocstyle. Man kan gi scriptet spesifikke .py-filer, hvis ikke sjekker den alle .py-filer.
+Sjekk av kodekvalitet gjøres med scriptet "CodeQualityCheck" i script-mappa, og er skrevet i Powershell(.ps1) og Bash(.sh). Scriptet kjører Pylint og Flake8 med tilhørende konfigurasjonsfiler, Pydocstyle med numpy-konvensjon og Bandit på Python-filer. 
 
-Terminal kjøres fra root:
+Scriptet godtar kommandolinjeargumenter: .py-fil(er), mappe(r ) eller en blanding av disse. Uten argumenter vil scriptet sjekke alle .py-filer.
 
-* Sjekk alle .py: `.\scripts\CodeQualityCheck.ps1` eller `./scripts/CodeQualityCheck.sh`
+Terminal kjøres fra root. Sjekk:
 
-* Sekk spesifikk(e) .py: `.\scripts\CodeQualityCheck.ps1 filEn.py filTo.py`
+* Alle .py-filer: `.\scripts\CodeQualityCheck.ps1` eller `./scripts/CodeQualityCheck.sh`
+
+* Spesifikk(e) .py: `.\scripts\CodeQualityCheck.ps1 filEn.py filTo.py`
+
+* Alle .py i mappe(r ): `.\scripts\CodeQualityCheck.ps1 mappeEn mappeTo`
+
+* Blanding: `.\scripts\CodeQualityCheck.ps1 mappeEn mappeTo\fil.py`
 
 ## Testing
 
diff --git a/scripts/.pylintrc b/scripts/.pylintrc
index 33b7a3368cc96aae954df72b4310d207348941ab..687b3068dd56f50726927545445e5430949ee7da 100644
--- a/scripts/.pylintrc
+++ b/scripts/.pylintrc
@@ -412,7 +412,9 @@ good-names=app,
            k,
            ex,
            Run,
-           _
+           _,
+           x,
+           y
 
 # Include a hint for the correct naming format with invalid-name.
 include-naming-hint=no
diff --git a/scripts/CodeQualityCheck.ps1 b/scripts/CodeQualityCheck.ps1
index 7e5021fa42af906c9517997851afcde34e009cd4..e2894fa734a9e2054caca683ea971f57eb0119d5 100644
--- a/scripts/CodeQualityCheck.ps1
+++ b/scripts/CodeQualityCheck.ps1
@@ -1,13 +1,24 @@
-
 if ($args.Count -eq 0){
 
     $files=Get-ChildItem -recurse | 
-           Where-Object {$_.name -match "[A-Za-z0-9_]+\.py" -And $_.Name -notmatch "pyc"} | 
+           Where-Object {$_.name -match "[A-Za-z0-9_]+\.py" -And $_.Name -notmatch ".pyc"} | 
            % { $_.FullName }
     Write-Output "`nChecking all .py-files"
 }
 else{
-    $files=$args
+    $files = @()
+
+    for($i=0; $i -lt $args.Length; $i++){
+
+        if($args[$i] -match "[A-Za-z0-9_]+\.py"){
+            $files += $args[$i]
+        }
+        else{
+            $files += Get-ChildItem -recurse $args[$i] | 
+            Where-Object {$_.Name -match "[A-Za-z0-9_]+\.py" -And $_.Name -notmatch ".pyc"} |
+            % { $_.FullName }
+        }
+    }
 }
 
 for ($i=0; $i -lt $files.Length; $i++){
diff --git a/scripts/CodeQualityCheck.sh b/scripts/CodeQualityCheck.sh
index 965530d50304173c177533c918d537ed4e3120d4..899f9a49d7074d873a2e6ded71a2db4e8efd049c 100644
--- a/scripts/CodeQualityCheck.sh
+++ b/scripts/CodeQualityCheck.sh
@@ -1,19 +1,27 @@
-
 if [[ $# -eq 0 ]]; then
     files=$(find -type f -name '*.py' )
     echo "\nChecking all .py-files"
 else
-    files=$@
-fi
+    args=$@
+    
+    for file in "${args[@]}"; do
 
-    for fileName in $files; do
-        printf "============================$fileName============================\n"
-        printf "\n===PYLINT===\n"
-        pylint --rcfile=./scripts/.pylintrc $fileName
-        printf "===FLAKE8===\n"
-        flake8 --config ./scripts/.flake8 $fileName
-        printf "\n===BANDIT===\n"
-        bandit $fileName
-        printf "\n===PYDOCSTYLE===\n"
-        pydocstyle --convention=numpy $fileName
+        if [[ $file =~ ^[A-Za-z0-9_/]+.py ]] && ! [[ $file =~ .pyc ]]; then
+            files+=( "$file" )
+        else
+            files+=$(find $file -type f -name '*.py')
+        fi
     done
+fi
+
+for file in $files; do
+    printf "========================$(basename -- $file)========================\n"
+    printf "\n===PYLINT===\n"
+    pylint --rcfile=./scripts/.pylintrc $file
+    printf "===FLAKE8===\n"
+    flake8 --config ./scripts/.flake8 $file
+    printf "\n===BANDIT===\n"
+    bandit $file
+    printf "\n===PYDOCSTYLE===\n"
+    pydocstyle --convention=numpy $file
+done
diff --git a/soitool/modules/__init__.py b/soitool/modules/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2927ebf2095d5e9fbff2ced1bfcb3f550716ff91
--- /dev/null
+++ b/soitool/modules/__init__.py
@@ -0,0 +1 @@
+"""All modules and moduleBase (interface)."""
diff --git a/soitool/modules/module_base.py b/soitool/modules/module_base.py
new file mode 100644
index 0000000000000000000000000000000000000000..92e7e6bc6145a1a1921cb5580c7b8384d77cad51
--- /dev/null
+++ b/soitool/modules/module_base.py
@@ -0,0 +1,14 @@
+"""Base/interface of each module."""
+from abc import ABC
+
+
+class ModuleBase(ABC):
+    """Interface for SOI-modules."""
+
+    def get_size(self):
+        """Abstract method, should be implemented by derived class."""
+        raise NotImplementedError
+
+    def render_onto_pdf(self):
+        """Abstract method, should be implemented by derived class."""
+        raise NotImplementedError
diff --git a/soitool/modules/module_table.py b/soitool/modules/module_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..5931f44367ab453627696db75da18337498496c9
--- /dev/null
+++ b/soitool/modules/module_table.py
@@ -0,0 +1,158 @@
+"""Module containing subclassed SOIModule (QTableWidget, ModuleBase)."""
+from PySide2.QtWidgets import QTableWidget, QTableWidgetItem, QShortcut
+from PySide2 import QtGui, QtCore
+from soitool.modules.module_base import ModuleBase
+
+HEADER_FONT = QtGui.QFont()
+HEADER_FONT.setFamily('Arial')
+HEADER_FONT.setPointSize(12)
+HEADER_FONT.setWeight(100)
+
+START_COLUMNS = 2
+START_ROWS = 2
+
+
+class Meta(type(ModuleBase), type(QTableWidget)):
+    """Used as a metaclass to enable multiple inheritance."""
+
+
+class TableModule(ModuleBase, QTableWidget, metaclass=Meta):
+    """Modified QTableWidget.
+
+    Has shortcuts for adding and removing rows and columns.
+    Inherits from ModuleBase and QTableWidget.
+    Functions inherited from ModuleBase are overridden by this class.
+
+    Has header-styled first row.
+    Functionality for adding and removing columns and rows is implemented.
+    """
+
+    def __init__(self):
+        """Initialize QTableWidget."""
+        QTableWidget.__init__(self)
+        super(QTableWidget)
+
+        # Remove headers
+        self.horizontalHeader().hide()
+        self.verticalHeader().hide()
+
+        # Set number of columns and rows
+        self.setColumnCount(START_COLUMNS)
+        self.setRowCount(START_ROWS)
+
+        # Resize width and height of columns and rows, and set size of window
+        self.resize()
+        self.setFixedWidth(START_COLUMNS * self.columnWidth(0) + 2)
+        self.setFixedHeight(START_ROWS * self.rowHeight(0) + 5)
+
+        # Remove scrollbars
+        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+
+        # Set headers
+        for i in range(self.columnCount()):
+            self.set_header_item(i, "")
+
+        self.cellChanged.connect(self.resize)
+
+        self.set_shortcuts()
+
+    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(HEADER_FONT)
+        self.setItem(0, column, item)
+
+    def set_shortcuts(self):
+        """Set shortcuts for adding and removing rows and columns."""
+        # Create shortcuts
+        shortcut_add_col = QShortcut(QtGui.QKeySequence("Shift++"), self)
+        shortcut_rem_col = QShortcut(QtGui.QKeySequence("Shift+-"), self)
+        shortcut_add_row = QShortcut(QtGui.QKeySequence("Ctrl++"), self)
+        shortcut_rem_row = QShortcut(QtGui.QKeySequence("Ctrl+-"), self)
+
+        # Connect shortcuts to functions
+        shortcut_add_col.activated.connect(self.add_column)
+        shortcut_rem_col.activated.connect(self.remove_column)
+        shortcut_add_row.activated.connect(self.add_row)
+        shortcut_rem_row.activated.connect(self.remove_row)
+
+    def add_column(self):
+        """Add column to the right of selected column."""
+        self.insertColumn(self.currentColumn() + 1)
+        self.set_header_item(self.currentColumn() + 1, "")
+        self.resize()
+
+    def remove_column(self):
+        """Remove selected column if two or more columns exist."""
+        if self.columnCount() > 1:
+            self.removeColumn(self.currentColumn())
+            self.resize()
+
+    def add_row(self):
+        """Add row below selected row."""
+        self.insertRow(self.currentRow() + 1)
+        self.resize()
+
+    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())
+            self.resize()
+
+    def resize(self):
+        """Resize widget, rows and columns.
+
+        Resize widget size to total width and height of rows and columns.
+        Resize rows and columns to contents.
+        """
+        self.resizeColumnsToContents()
+        self.resizeRowsToContents()
+
+        # Calculate total width and height of columns and rows
+        width = 0
+        height = 0
+
+        for x in range(self.columnCount()):
+            width += self.columnWidth(x) + 0.5
+
+        for y in range(self.rowCount()):
+            height += self.rowHeight(y) + 0.5
+
+        # Set total width and height
+        self.setFixedWidth(width)
+        self.setFixedHeight(height)
+
+    def get_size(self):
+        """Get size of widget.
+
+        Returns
+        -------
+        Tuple
+            (width, height) (total)
+        """
+        # Calculate total width and height of columns and rows
+        width = 0
+        height = 0
+
+        for i in range(self.columnCount()):
+            width += self.columnWidth(i) + 0.5
+
+        for i in range(self.columnCount()):
+            height += self.rowHeight(i) + 0.5
+
+        return width, height
+
+    def render_onto_pdf(self):
+        """Render onto pdf."""