diff --git a/Dockerfile b/Dockerfile
index 0ba5c5605dc81669c396b2cc9f78f6538410168d..d4420c91c03b1d3436a69ceeb553db22250e6a8a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -41,3 +41,11 @@ ENV LC_ALL en_US.UTF-8
 # From https://it.i88.ca/2016/03/how-to-fix-importerror-libgssapikrb5so2.html
 # python3.7 -m unittest fails without this
 RUN apt-get -y install libgssapi-krb5-2
+
+# Necessary for import of PySide2.QtWebEngineWidgets to work
+RUN apt-get -y install libnss3 \
+                       libxcomposite-dev \
+                       libxcursor-dev \
+                       libxi-dev \
+                       libxtst-dev \
+                       libxrandr-dev
diff --git a/requirements.txt b/requirements.txt
index 30b831698a7525835953ccaf5c31a5f05f5eab68..990f903ec1aa9d48d5beb0b62fcafaedd49419a8 100644
Binary files a/requirements.txt and b/requirements.txt differ
diff --git a/scripts/.flake8 b/scripts/.flake8
index de75a936c7acec77595ab58ce170803ae7205cd2..f6c974480c4192afdbffa9c58c1b72a51d49339e 100644
--- a/scripts/.flake8
+++ b/scripts/.flake8
@@ -2,4 +2,4 @@
 ; ignorerer sjekk for lange linjer, ettersom dette gjøres i pylint
 ; vi vil tillate lange linjer som avsluttes med link, og dette er mulig i
 ; pylint, men såvidt vi vet ikke mulig i flake8
-ignore = E501
+ignore = E501,W503
diff --git a/soitool/main_window.py b/soitool/main_window.py
index cbdc6433fa0a3184d6672619b191c6ae5545ace2..7f6214e98a183def32071c333ba676a846757e10 100644
--- a/soitool/main_window.py
+++ b/soitool/main_window.py
@@ -6,11 +6,12 @@ out to separate modules.
 """
 import sys
 import os
-from PySide2.QtCore import QRectF, QPoint, QTimer, Qt
+from PySide2.QtCore import QRectF, QPoint, QTimer, Qt, QUrl
 from PySide2.QtWidgets import QTabWidget, QWidget, QMainWindow, \
     QApplication, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, \
     QAbstractItemView, QListWidget, QListWidgetItem, QAction, QGraphicsScene, \
     QGraphicsView, QScrollArea, QGraphicsRectItem
+from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
 from PySide2.QtGui import QBrush, QIcon, QPalette, QFont, QPixmap
 from soitool.modules.module_table import TableModule
 
@@ -648,6 +649,36 @@ class ModuleList(QListWidget):
         self.original_element_index = self.currentRow()
 
 
+# pylint: disable=r0903
+class PDFPreviewWidget(QWebEngineView):
+    """Widget to preview PDFs using QWebEngineView with PDF plugin.
+
+    Inspired by the following sources:
+    * https://doc.qt.io/qt-5/qtwebengine-features.html#pdf-file-viewing
+    * https://doc.qt.io/qt-5/qtwebengine-webenginewidgets-simplebrowser-example.html
+
+    The load method interited from QWebEngineView's can be used to load a new
+    URL, and the inherited reload method can be used to reload the currently
+    opened URL.
+
+    Parameters
+    ----------
+    initial_url : QUrl
+        URL to load by default. Used for QWebEngineView's .load method
+        https://doc.qt.io/qtforpython/PySide2/QtWebEngineWidgets/QWebEngineView.html#detailed-description
+    """
+
+    def __init__(self, initial_url):
+        super().__init__()
+        self.page().settings().setAttribute(QWebEngineSettings.PluginsEnabled,
+                                            True)
+        # the following setting is the default, but explicitly enabling to be
+        # explicit
+        self.page().settings().setAttribute(QWebEngineSettings.
+                                            PdfViewerEnabled, True)
+        self.load(QUrl(initial_url))
+
+
 if __name__ == "__main__":
     app = QApplication(sys.argv)
     WINDOW = MainWindow()
diff --git a/soitool/modules/module_table.py b/soitool/modules/module_table.py
index e9d06a7502c15d4d859c0173eb39ccc7de5dd481..29adbce0881c19645afa7c8e0435eefc6748a197 100644
--- a/soitool/modules/module_table.py
+++ b/soitool/modules/module_table.py
@@ -1,5 +1,5 @@
 """Module containing subclassed SOIModule (QTableWidget, ModuleBase)."""
-from PySide2.QtWidgets import QTableWidget, QTableWidgetItem, QShortcut
+from PySide2.QtWidgets import QTableWidget, QTableWidgetItem
 from PySide2 import QtGui, QtCore
 from soitool.modules.module_base import ModuleBase
 
@@ -28,7 +28,13 @@ class TableModule(ModuleBase, QTableWidget, metaclass=Meta):
     """
 
     def set_pos(self, pos):
-        """Set position of widget."""
+        """Set position of widget.
+
+        Parameters
+        ----------
+        pos : QPoint
+            Position (x, y).
+        """
         self.move(pos)
 
     def __init__(self):
@@ -58,7 +64,31 @@ class TableModule(ModuleBase, QTableWidget, metaclass=Meta):
 
         self.cellChanged.connect(self.resize)
 
-        self.set_shortcuts()
+    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(TableModule, self).keyPressEvent(event)
 
     def set_header_item(self, column, text):
         """Insert item with header-style.
@@ -77,20 +107,6 @@ class TableModule(ModuleBase, QTableWidget, metaclass=Meta):
         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)