class SimilarityPlugin:
    """
    Similarity Plugin parent class
    
    ..

    Attributes
    -----------

    dlg : SimilarityPluginDialog
        MainPluginDialog
    simpleDialog : SimpleWarningDialog
        Show simple warning
    similarLayer : list=[]
        The result of calculation process
    previewLayer: int=0
        Current index similarLayer that previewed in canvas widget  
    calcThread : QThread(self.iface)
        Thread for data processing
    calcTask : CalculationModule
        Calculation module for checking similarity
    iface : QgsInterface
        An interface instance that will be passed to this class
        which provides the hook by which you can manipulate the QGIS
        application at run time.

    """

    layer: QgsVectorLayer  #: First layer
    layer2: QgsVectorLayer  #: Second layer
    simpleDialog: SimpleWarnDialog  #: Simple warning dialog

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        self.similarLayer = []
        """  """
        self.previewLayer = 0
        self.calcTask = CalculationModule()
        # Save reference to the QGIS interface
        self.iface = iface

        # Registering worker calculation
        self.calcThread = QThread(self.iface)
        self.calcTask.moveToThread(self.calcThread)
        self.calcThread.started.connect(self.calcTask.run)
        self.calcThread.setTerminationEnabled(True)

        # multithreading signal calculation
        self.calcTask.progress.connect(self.updateCalcProgress)
        self.calcTask.progressSim.connect(self.updateSimList)
        self.calcTask.finished.connect(self.finishedCalcThread)
        self.calcTask.error.connect(self.errorCalcThread)
        self.calcTask.eventTask.connect(self.eventCalcThread)

        # pan event
        self.actionPan = QAction("Pan", self.iface)

        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'SimilarityPlugin_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Calculate Similarity Map')

        # Check if plugin was started the first time in current QGIS session
        # Must be set in initGui() to survive plugin reloads
        self.first_start = None

    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('SimilarityPlugin', message)

    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToVectorMenu(self.menu, action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/similarity_plugin/icon-24.png'
        self.add_action(icon_path,
                        text=self.tr(u'Check Similarity ...'),
                        callback=self.run,
                        parent=self.iface.mainWindow())

        # will be set False in run()
        self.first_start = True

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginVectorMenu(
                self.tr(u'&Calculate Similarity Map'), action)
            self.iface.removeToolBarIcon(action)

    def methodChange(self):
        """Signal when method changed"""
        if self.dlg.methodComboBox.currentIndex() == 2:
            # self.dlg.mergeCenterCheck.setChecked(False)
            # self.dlg.setPKBtn.setVisible(True)
            self.dlg.mergeCenterCheck.setEnabled(True)
            self.dlg.lineEditTreshold.setEnabled(False)
            self.dlg.nnRadiusEdit.setEnabled(False)
            # self.pkSelector.layerListWidget.clear()
            # self.pkSelector.layerListWidget.addItems(
            #     self.dlg.layerSel1.currentLayer().fields().names()
            # )
            # self.pkSelector.layer2ListWidget.clear()
            # self.pkSelector.layer2ListWidget.addItems(
            #     self.dlg.layerSel2.currentLayer().fields().names()
            # )
            # self.pkSelector.open()
        elif self.dlg.methodComboBox.currentIndex() == 0:
            self.dlg.mergeCenterCheck.setChecked(False)
            self.dlg.mergeCenterCheck.setEnabled(False)
            # self.dlg.setPKBtn.setVisible(False)
            self.dlg.lineEditTreshold.setEnabled(True)
            self.dlg.nnRadiusEdit.setEnabled(False)
        elif self.dlg.methodComboBox.currentIndex() == 1:
            self.dlg.mergeCenterCheck.setChecked(True)
            self.dlg.mergeCenterCheck.setEnabled(False)
            # self.dlg.setPKBtn.setVisible(False)
            self.dlg.lineEditTreshold.setEnabled(True)
            self.dlg.nnRadiusEdit.setEnabled(True)

    def resultPreview(self):
        """Activate preview section

        This method will called if calculation process is finished

        See Also
        ----------
            refreshPreview()
            SimilarityPluginDialog.widgetCanvas
            SimilarityPluginDialog.nextBtn
            SimilarityPluginDialog.previousBtn
            SimilarityPluginDialog.removeBtn

        """
        self.previewLayer = 0
        self.refreshPreview()

        self.dlg.widgetCanvas.enableAntiAliasing(True)

        self.dlg.nextBtn.setEnabled(True)
        self.dlg.previousBtn.setEnabled(True)
        self.dlg.removeBtn.setEnabled(True)

    def attrPrinter(self, fieldsList: object, feature: QgsFeature,
                    place: QTextEdit):
        """print the attribute table on preview panel

        :param fieldsList object: List the attribute value of feature
        :param feature QgsFeature: The feature will be printed
        :param place QTextEdit: The place for editing text
        
        """
        temp = ''

        for f in fieldsList:
            temp += f.name()
            temp += ' : '
            temp += str(feature.attribute(f.name()))
            temp += '\n'
        # print(place)
        place.setText(temp)

    def refreshPreview(self):
        """refreshing canvas on preview"""
        if len(self.similarLayer) > 0:
            # set the layer
            self.layerCanvas = QgsVectorLayer("Polygon?crs=ESPG:4326",
                                              'SimilarityLayer', 'memory')
            self.layer2Canvas = QgsVectorLayer("Polygon?crs=ESPG:4326",
                                               'SimilarityLayer', 'memory')

            # set the feature
            previewLayerFeature = self.calcTask.getLayersDup()[0].getFeature(
                self.similarLayer[self.previewLayer][0])
            previewLayerFeature2 = self.calcTask.getLayersDup()[1].getFeature(
                self.similarLayer[self.previewLayer][1])

            # set the label score on preview
            scoreLabel = "Score : " + str(
                round(self.similarLayer[self.previewLayer][2], 3))

            # set cumulative score on preview
            if (not self.calcTask.getTranslate()):
                scoreLabel += " - Cumulative score : " + str(
                    self.calcTask.getCumulative(
                        self.similarLayer[self.previewLayer]))

            # show distance if the layer merge centered (NN and WK only)
            if (self.dlg.methodComboBox.currentIndex() == 1
                    or self.dlg.methodComboBox.currentIndex() == 2):
                distance = QgsGeometry.distance(
                    previewLayerFeature.geometry().centroid(),
                    previewLayerFeature2.geometry().centroid())
                if distance < 0.00001:
                    distance = 0
                self.dlg.labelScore.setText(scoreLabel + " - Distance : " +
                                            str(round(distance, 3)))
            else:
                self.dlg.labelScore.setText(scoreLabel)

            self.attrPrinter(
                self.calcTask.getLayersDup()
                [0].dataProvider().fields().toList(), previewLayerFeature,
                self.dlg.previewAttr)
            self.attrPrinter(
                self.calcTask.getLayersDup()
                [1].dataProvider().fields().toList(), previewLayerFeature2,
                self.dlg.previewAttr_2)

            self.layerCanvas.dataProvider().addFeature(previewLayerFeature)

            # translating preview
            if self.calcTask.getTranslate():
                tGeom = self.calcTask.translateCenterGeom(
                    previewLayerFeature2.geometry(),
                    previewLayerFeature.geometry())
                nFeat = QgsFeature(previewLayerFeature2)
                nFeat.setGeometry(tGeom)
                self.layer2Canvas.dataProvider().addFeature(nFeat)
            else:
                self.layer2Canvas.dataProvider().addFeature(
                    previewLayerFeature2)

            # set canvas to preview feature layer
            self.dlg.widgetCanvas.setExtent(
                previewLayerFeature.geometry().boundingBox(), True)

            self.dlg.widgetCanvas.setDestinationCrs(
                self.layerCanvas.sourceCrs())

            symbol = self.layerCanvas.renderer().symbol()
            symbol.setColor(QColor(0, 147, 221, 127))

            symbol2 = self.layer2Canvas.renderer().symbol()
            symbol2.setColor(QColor(231, 120, 23, 127))

            self.dlg.widgetCanvas.setLayers(
                [self.layer2Canvas, self.layerCanvas])

            # redraw the canvas
            self.dlg.widgetCanvas.refresh()

    def nextPreview(self):
        """Next preview signal for next button in preview section"""
        # f2 = open("engine/f2.txt", "w")
        if (self.previewLayer < len(self.similarLayer) - 1):
            self.previewLayer = int(self.previewLayer) + 1
        # self.dlg.consoleTextEdit.setText(self.dlg.consoleTextEdit.toPlainText()+"\n\n Current Similar Layer Index : \n  "+str([self.similarLayer[self.previewLayer], self.previewLayer]))
        self.refreshPreview()

    def previousPreview(self):
        """Previous preview signal"""
        if (self.previewLayer > 0):
            self.previewLayer = int(self.previewLayer) - 1
        # self.dlg.consoleTextEdit.setText(self.dlg.consoleTextEdit.toPlainText()+"\n\n Current Similar Layer Index : \n  "+str([self.similarLayer[self.previewLayer], self.previewLayer]))
        self.refreshPreview()

    def rmFeatResult(self):
        """Removing similarity info current result"""
        self.similarLayer.pop(self.previewLayer)
        if (self.previewLayer > len(self.similarLayer)):
            self.previewLayer = len(self.similarLayer - 1)
        self.refreshPreview()
        self.warnDlg.close()

    def rmWarn(self):
        """prevention remove item preview"""
        self.warnDlg = self.warnDialogInit(
            'Are you sure to delete this feature ?')
        self.warnDlg.yesBtn.clicked.connect(self.rmFeatResult)
        self.warnDlg.noBtn.clicked.connect(self.warnDlg.close)
        self.warnDlg.show()

    def updateCalcProgress(self, value):
        """Progress signal for calcTask"""
        self.dlg.progressBar.setValue(int(round(value, 1)))

    def updateSimList(self, simList: list):
        """Updating similiarity result signal"""
        self.similarLayer.append(simList)
        cText = "Number of Result: " + str(len(self.similarLayer))
        self.dlg.counterLabel.setText(cText)

    # thread signal
    def errorCalcThread(self, value: str):
        """Signal when an error occured"""
        print("error : ", value)
        self.dlg.consoleTextEdit.append("error : " + value + "\n\n")
        self.simpleWarnDialogInit(value)
        self.dlg.calcBtn.setEnabled(True)
        self.dlg.stopBtn.setEnabled(False)

    def finishedCalcThread(self, itemVal: list):
        """signal when calcTask calculation is finished

        :param itemVal list: the returned value emit
        
        """
        # print("finished returned : ", itemVal)
        # self.similarLayer = itemVal
        # self.setLayers(self.calcTask.getLayersDup())
        self.calcThread.exit()
        self.calcTask.kill()
        cText = "Number of Result: " + str(len(self.similarLayer))
        self.dlg.consoleTextEdit.append(cText + "\n\n")
        if len(self.similarLayer) > 0:
            # self.addScoreItem()
            self.dlg.consoleTextEdit.append(
                "The 2 vector layer has been checked\n\n")
            self.previewLayer = 0
            self.dlg.saveBtn.setEnabled(True)
            self.dlg.counterLabel.setText(cText)
            self.resultPreview()
        else:
            self.previewLayer = 0
            self.dlg.counterLabel.setText(cText)
        self.dlg.calcBtn.setEnabled(True)
        self.dlg.stopBtn.setEnabled(False)

    def stopCalcThread(self):
        """Signal when calcTask is stopped """
        self.calcThread.exit()
        self.dlg.eventLabel.setText("Event: Stopped")
        self.calcTask.kill()
        if (self.calcTask.getLayersDup()[0].featureCount() > 0
                and self.calcTask.getLayersDup()[1].featureCount() > 0):
            cText = "Number of Result: " + str(len(self.similarLayer))
            self.dlg.consoleTextEdit.append(cText + "\n\n")
            if len(self.similarLayer) > 0:
                # self.addScoreItem()
                self.previewLayer = 0
                self.dlg.saveBtn.setEnabled(True)
                self.dlg.counterLabel.setText(cText)
                self.resultPreview()
            else:
                self.previewLayer = 0
                self.dlg.counterLabel.setText(cText)
            self.dlg.calcBtn.setEnabled(True)
            self.dlg.stopBtn.setEnabled(False)

    def eventCalcThread(self, value: str):
        """Receiving signal event
        
        :param value str: the returned value emit
        
        """
        self.dlg.eventLabel.setText("Event: " + value)

    # executing calculation
    def calculateScore(self):
        """Signal for executing calculation for cheking maps"""
        if (isinstance(self.dlg.layerSel1.currentLayer(), QgsVectorLayer) and
                isinstance(self.dlg.layerSel1.currentLayer(), QgsVectorLayer)):
            # set plugin to initial condition
            self.dlg.progressBar.setValue(0)
            self.dlg.saveBtn.setEnabled(False)
            self.dlg.nextBtn.setEnabled(False)
            self.dlg.previousBtn.setEnabled(False)
            self.dlg.removeBtn.setEnabled(False)
            self.dlg.widgetCanvas.setLayers([
                QgsVectorLayer("Polygon?crs=ESPG:4326", 'SimilarityLayer',
                               'memory')
            ])
            self.dlg.previewAttr.setText("")
            self.dlg.previewAttr_2.setText("")
            self.dlg.widgetCanvas.refresh()
            scoreLabel = "Score : 0"
            self.dlg.counterLabel.setText("Number of Result: 0")
            self.dlg.labelScore.setText(scoreLabel)
            self.similarLayer = []

            # set input-output option
            self.calcTask.setLayers(self.dlg.layerSel1.currentLayer(),
                                    self.dlg.layerSel2.currentLayer())
            self.calcTask.setTreshold(self.dlg.lineEditTreshold.value())
            self.calcTask.setMethod(int(
                self.dlg.methodComboBox.currentIndex()))
            self.calcTask.setTranslate(self.dlg.mergeCenterCheck.isChecked())
            self.calcTask.setRadius(self.dlg.nnRadiusEdit.value())
            self.calcTask.setSuffix(str(self.dlg.sufLineEdit.text()))
            self.calcTask.setScoreName(str(self.dlg.attrOutLineEdit.text()))
            # print("input option set")
            # activating task
            self.calcTask.alive()
            # print("task alive")
            self.calcThread.start()
            # print("thread started")

            # set button
            self.dlg.calcBtn.setEnabled(False)
            self.dlg.stopBtn.setEnabled(True)
        else:
            # prevention on QgsVectorLayer only
            self.simpleWarnDialogInit("This plugin support Vector Layer only")

    # signal when saveBtn clicked
    def registerToProject(self):
        """Signal to registering project"""
        QgsProject.instance().addMapLayers(self.calcTask.getLayersResult())

    # warning dialog for error or prevention
    def warnDialogInit(self, msg: str):
        """This dialog have Yes and No button.
        :param msg: str Display the warning message 
        """

        dialog = WarnDialog()
        #set the message
        dialog.msgLabel.setText(msg)
        return dialog

    # initializing simple warning dialog
    def simpleWarnDialogInit(self, msg: str):
        """ This dialog have ok button only
        
        :param: msg str: Display the warning message 
        
        """

        # Set the message
        self.simpleDialog.msgLabel.setText(msg)
        self.simpleDialog.show()

    # def pkSelectorAccepted(self):
    #     if( len(self.pkSelector.layerListWidget.selectedItems()) > 0 and len(self.pkSelector.layer2ListWidget.selectedItems()) > 0 and
    #             (len(self.pkSelector.layerListWidget.selectedItems()) == len(self.pkSelector.layer2ListWidget.selectedItems()))
    #         ):
    #         names = [j.text() for j in self.pkSelector.layerListWidget.selectedItems()]
    #         names.sort()
    #         names2 = [j.text() for j in self.pkSelector.layer2ListWidget.selectedItems()]
    #         names2.sort()
    #         print(names)
    #         print(names2)
    #         self.pkSelector.accept()
    #     else:
    #         self.simpleWarnDialogInit("Primary Key must be same in length as key or not null")

    def run(self):
        """Run method that performs all the real work"""

        # print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())

        # init run variable
        # self.canvas = QgsMapCanvas()

        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.previewLayer = 0
            self.currentCheckLayer = [0, 0]
            self.first_start = False
            self.dlg = SimilarityPluginDialog()
            # self.dlg.setPKBtn.setVisible(False)
            self.simpleDialog = SimpleWarnDialog()
            # self.pkSelector = PkSelector()
            # set help documentation
            self.dlg.helpTextBrowser.load(
                QUrl(
                    'https://github.com/panickspa/SimilarityPlugin/wiki/User-Guide'
                ))
            self.dlg.nextHelpBtn.clicked.connect(
                self.dlg.helpTextBrowser.forward)
            self.dlg.previousHelpBtn.clicked.connect(
                self.dlg.helpTextBrowser.back)
            # filtering selection layer (empty layer not allowed)
            self.dlg.layerSel1.setAllowEmptyLayer(False)
            self.dlg.layerSel1.setAllowEmptyLayer(False)

            # self.pkSelector.okPushButton.clicked.connect(self.pkSelectorAccepted)
            # self.dlg.setPKBtn.clicked.connect(self.pkSelector.open)
            # method combobox initialiazation
            self.dlg.methodComboBox.clear()
            self.dlg.methodComboBox.addItems(
                ['Squential', 'Nearest Neightbour', 'Wilkerstat BPS'])

            # registering signal

            self.dlg.methodComboBox.currentIndexChanged.connect(
                self.methodChange)
            self.dlg.nextBtn.clicked.connect(self.nextPreview)
            self.dlg.previousBtn.clicked.connect(self.previousPreview)
            self.dlg.calcBtn.clicked.connect(self.calculateScore)
            self.dlg.saveBtn.clicked.connect(self.registerToProject)
            self.dlg.removeBtn.clicked.connect(self.rmWarn)
            self.dlg.stopBtn.clicked.connect(self.stopCalcThread)

            # intialize pan tool
            panTool = QgsMapToolPan(self.dlg.widgetCanvas)
            # set signal
            panTool.setAction(self.actionPan)
            # set map tool
            self.dlg.widgetCanvas.setMapTool(panTool)
            # set pan tool to be activate
            panTool.activate()

        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            self.similarLayer = []
            self.dlg.widgetCanvas.setLayers([
                QgsVectorLayer("Polygon?crs=ESPG:4326", 'SimilarityLayer',
                               'memory')
            ])
            self.dlg.previewAttr.setText("")
            self.dlg.previewAttr_2.setText("")
            self.dlg.widgetCanvas.refresh()
            scoreLabel = "Score : 0"
            self.dlg.labelScore.setText(scoreLabel)
Ejemplo n.º 2
0
class OpenTripPlannerPlugin():
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """

        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir, 'i18n',
            'OpenTripPlannerPlugin_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&OpenTripPlanner Plugin')

        # Check if plugin was started the first time in current QGIS session
        # Must be set in initGui() to survive plugin reloads
        self.first_start = None

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('OpenTripPlannerPlugin', message)

    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/otp_plugin/icon.png'
        self.add_action(icon_path,
                        text=self.tr(u'OpenTripPlanner Plugin'),
                        callback=self.run,
                        parent=self.iface.mainWindow())

        # will be set False in run()
        self.first_start = True

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(self.tr(u'&OpenTripPlanner Plugin'),
                                        action)
            self.iface.removeToolBarIcon(action)

    def isochronesStartWorker(self):  # method to start the worker thread
        if not self.gf.isochrones_selectedlayer:  # dont execute if no layer is selected
            QgsMessageLog.logMessage(
                "Warning! No inputlayer selected. Choose an inputlayer and try again.",
                MESSAGE_CATEGORY, Qgis.Critical)
            self.iface.messageBar().pushMessage(
                "Warning",
                " No inputlayer selected. Choose an inputlayer and try again.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
            return
        if self.gf.isochrones_selectedlayer.featureCount() == 0:
            QgsMessageLog.logMessage(
                "Warning! Inputlayer is empty. Add some features and try again.",
                MESSAGE_CATEGORY, Qgis.Critical)
            self.iface.messageBar().pushMessage(
                "Warning",
                " Inputlayer is empty. Add some features and try again.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
            return
        isochrones_memorylayer_vl = QgsVectorLayer(
            "MultiPolygon?crs=epsg:4326", "Isochrones",
            "memory")  # Create temporary polygon layer (output file)
        self.isochrones_thread = QThread()
        self.isochrones_worker = OpenTripPlannerPluginIsochronesWorker(
            self.dlg, self.iface, self.gf, isochrones_memorylayer_vl)
        # see https://realpython.com/python-pyqt-qthread/#using-qthread-to-prevent-freezing-guis
        # and https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html
        self.isochrones_worker.moveToThread(
            self.isochrones_thread)  # move Worker-Class to a thread
        # Connect signals and slots:
        self.isochrones_thread.started.connect(self.isochrones_worker.run)
        self.isochrones_worker.isochrones_finished.connect(
            self.isochrones_thread.quit)
        self.isochrones_worker.isochrones_finished.connect(
            self.isochrones_worker.deleteLater)
        self.isochrones_thread.finished.connect(
            self.isochrones_thread.deleteLater)
        self.isochrones_worker.isochrones_progress.connect(
            self.isochronesReportProgress)
        self.isochrones_worker.isochrones_finished.connect(
            self.isochronesFinished)
        self.isochrones_thread.start()  # finally start the thread
        # Disable/Enable GUI elements to prevent them from beeing used while worker threads are running and accidentially changing settings during progress
        self.gf.disableIsochronesGui()
        self.gf.disableGeneralSettingsGui()
        self.isochrones_thread.finished.connect(
            lambda: self.gf.enableIsochronesGui())
        self.isochrones_thread.finished.connect(
            lambda: self.gf.enableGeneralSettingsGui())

    def isochronesKillWorker(self):  # method to kill/cancel the worker thread
        # print('pushed cancel') # debugging
        # see https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html
        try:  # to prevent a Python error when the cancel button has been clicked but no thread is running use try/except
            self.isochrones_worker.stop(
            )  # call the stop method in worker class to break the work-loop so we can quit the thread
            if self.isochrones_thread.isRunning(
            ):  # check if a thread is running
                # print('pushed cancel, thread is running, trying to cancel') # debugging
                self.isochrones_thread.requestInterruption()
                self.isochrones_thread.exit(
                )  # Tells the thread’s event loop to exit with a return code.
                self.isochrones_thread.quit(
                )  # Tells the thread’s event loop to exit with return code 0 (success). Equivalent to calling exit (0).
                self.isochrones_thread.wait(
                )  # Blocks the thread until https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html#PySide6.QtCore.PySide6.QtCore.QThread.wait
        except:
            self.dlg.Isochrones_ProgressBar.setValue(0)
            self.dlg.Isochrones_StatusBox.setText('')

    def isochronesReportProgress(
            self, progress, status):  # method to report the progress to gui
        self.dlg.Isochrones_ProgressBar.setValue(
            progress)  # set the current progress in progress bar
        self.dlg.Isochrones_StatusBox.setText(status)

    def isochronesFinished(
        self,
        isochrones_resultlayer,
        isochrones_state,
        unique_errors="",
        runtime="00:00:00 (unknown)"
    ):  # method to interact with gui when thread is finished or canceled
        QgsProject.instance().addMapLayer(
            isochrones_resultlayer)  # Show resultlayer in project
        # isochrones_state is indicating different states of the thread/result as integer
        if unique_errors:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Errors occurred. Check the resultlayer for details. The errors were: "
                + unique_errors +
                " - Created dummy geometries at coordinate 0,0 on error features",
                MESSAGE_CATEGORY,
                level=Qgis.Warning,
                duration=12)
            QgsMessageLog.logMessage(
                "Errors occurred. Check the resultlayer for details. The errors were: "
                + unique_errors +
                " - Created dummy geometries at coordinate 0,0 on error features",
                MESSAGE_CATEGORY, Qgis.Warning)
        if isochrones_state == 0:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Run-Method was never executed.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
        elif isochrones_state == 1:
            self.iface.messageBar().pushMessage(
                "Done!",
                " Isochrones job finished after " + runtime,
                MESSAGE_CATEGORY,
                level=Qgis.Success,
                duration=6)
        elif isochrones_state == 2:
            self.iface.messageBar().pushMessage(
                "Done!",
                " Isochrones job canceled after " + runtime,
                MESSAGE_CATEGORY,
                level=Qgis.Success,
                duration=6)
        elif isochrones_state == 3:
            self.iface.messageBar().pushMessage(
                "Warning",
                " No Isochrones to create - Check your settings and retry.",
                MESSAGE_CATEGORY,
                level=Qgis.Warning,
                duration=6)
        elif isochrones_state == 99:
            self.iface.messageBar().pushMessage(
                "Debugging",
                " Just having some debugging fun :)",
                MESSAGE_CATEGORY,
                level=Qgis.Info,
                duration=6)
        else:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Unknown error occurred during execution.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)

    def aggregated_isochronesStartWorker(
            self):  # method to start the worker thread
        if not self.gf.aggregated_isochrones_selectedlayer:  # dont execute if no layer is selected
            QgsMessageLog.logMessage(
                "Warning! No inputlayer selected. Choose an inputlayer and try again.",
                MESSAGE_CATEGORY, Qgis.Critical)
            self.iface.messageBar().pushMessage(
                "Warning",
                " No inputlayer selected. Choose an inputlayer and try again.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
            return
        if self.gf.aggregated_isochrones_selectedlayer.featureCount() == 0:
            QgsMessageLog.logMessage(
                "Warning! Inputlayer is empty. Add some features and try again.",
                MESSAGE_CATEGORY, Qgis.Critical)
            self.iface.messageBar().pushMessage(
                "Warning",
                " Inputlayer is empty. Add some features and try again.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
            return
        aggregated_isochrones_memorylayer_vl = QgsVectorLayer(
            "MultiPolygon?crs=epsg:4326", "AggregatedIsochrones",
            "memory")  # Create temporary polygon layer (output file)
        self.aggregated_isochrones_thread = QThread()
        self.aggregated_isochrones_worker = OpenTripPlannerPluginAggregatedIsochronesWorker(
            self.dlg, self.iface, self.gf,
            aggregated_isochrones_memorylayer_vl)
        # see https://realpython.com/python-pyqt-qthread/#using-qthread-to-prevent-freezing-guis
        # and https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html
        self.aggregated_isochrones_worker.moveToThread(
            self.aggregated_isochrones_thread)  # move Worker-Class to a thread
        # Connect signals and slots:
        self.aggregated_isochrones_thread.started.connect(
            self.aggregated_isochrones_worker.run)
        self.aggregated_isochrones_worker.aggregated_isochrones_finished.connect(
            self.aggregated_isochrones_thread.quit)
        self.aggregated_isochrones_worker.aggregated_isochrones_finished.connect(
            self.aggregated_isochrones_worker.deleteLater)
        self.aggregated_isochrones_thread.finished.connect(
            self.aggregated_isochrones_thread.deleteLater)
        self.aggregated_isochrones_worker.aggregated_isochrones_progress.connect(
            self.aggregated_isochronesReportProgress)
        self.aggregated_isochrones_worker.aggregated_isochrones_finished.connect(
            self.aggregated_isochronesFinished)
        self.aggregated_isochrones_thread.start()  # finally start the thread
        # Disable/Enable GUI elements to prevent them from beeing used while worker threads are running and accidentially changing settings during progress
        self.gf.disableAggregatedIsochronesGui()
        self.gf.disableGeneralSettingsGui()
        self.aggregated_isochrones_thread.finished.connect(
            lambda: self.gf.enableAggregatedIsochronesGui())
        self.aggregated_isochrones_thread.finished.connect(
            lambda: self.gf.enableGeneralSettingsGui())

    def aggregated_isochronesKillWorker(
            self):  # method to kill/cancel the worker thread
        # print('pushed cancel') # debugging
        # see https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html
        try:  # to prevent a Python error when the cancel button has been clicked but no thread is running use try/except
            self.aggregated_isochrones_worker.stop(
            )  # call the stop method in worker class to break the work-loop so we can quit the thread
            if self.aggregated_isochrones_thread.isRunning(
            ):  # check if a thread is running
                # print('pushed cancel, thread is running, trying to cancel') # debugging
                self.aggregated_isochrones_thread.requestInterruption()
                self.aggregated_isochrones_thread.exit(
                )  # Tells the thread’s event loop to exit with a return code.
                self.aggregated_isochrones_thread.quit(
                )  # Tells the thread’s event loop to exit with return code 0 (success). Equivalent to calling exit (0).
                self.aggregated_isochrones_thread.wait(
                )  # Blocks the thread until https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html#PySide6.QtCore.PySide6.QtCore.QThread.wait
        except:
            self.dlg.AggregatedIsochrones_ProgressBar.setValue(0)
            self.dlg.AggregatedIsochrones_StatusBox.setText('')

    def aggregated_isochronesReportProgress(
            self, progress, status):  # method to report the progress to gui
        self.dlg.AggregatedIsochrones_ProgressBar.setValue(
            progress)  # set the current progress in progress bar
        self.dlg.AggregatedIsochrones_StatusBox.setText(status)

    def aggregated_isochronesFinished(
        self,
        aggregated_isochrones_resultlayer,
        aggregated_isochrones_state,
        unique_errors="",
        runtime="00:00:00 (unknown)"
    ):  # method to interact with gui when thread is finished or canceled
        QgsProject.instance().addMapLayer(
            aggregated_isochrones_resultlayer)  # Show resultlayer in project
        # aggregated_isochrones_state is indicating different states of the thread/result as integer
        if unique_errors:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Errors occurred. Check the resultlayer for details. The errors were: "
                + unique_errors,
                MESSAGE_CATEGORY,
                level=Qgis.Warning,
                duration=12)
            QgsMessageLog.logMessage(
                "Errors occurred. Check the resultlayer for details. The errors were: "
                + unique_errors, MESSAGE_CATEGORY, Qgis.Warning)
        if aggregated_isochrones_state == 0:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Run-Method was never executed.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
        elif aggregated_isochrones_state == 1:
            self.iface.messageBar().pushMessage(
                "Done!",
                " Isochrones job finished after " + runtime,
                MESSAGE_CATEGORY,
                level=Qgis.Success,
                duration=6)
        elif aggregated_isochrones_state == 2:
            self.iface.messageBar().pushMessage(
                "Done!",
                " Isochrones job canceled after " + runtime,
                MESSAGE_CATEGORY,
                level=Qgis.Success,
                duration=6)
        elif aggregated_isochrones_state == 3:
            self.iface.messageBar().pushMessage(
                "Warning",
                " No Isochrones to create - Check your settings and retry.",
                MESSAGE_CATEGORY,
                level=Qgis.Warning,
                duration=6)
        elif aggregated_isochrones_state == 4:
            self.iface.messageBar().pushMessage(
                "Warning",
                " There is something wrong with your DateTime-Settings, check them and try again.",
                MESSAGE_CATEGORY,
                level=Qgis.Warning,
                duration=6)
        elif aggregated_isochrones_state == 99:
            self.iface.messageBar().pushMessage(
                "Debugging",
                " Just having some debugging fun :)",
                MESSAGE_CATEGORY,
                level=Qgis.Info,
                duration=6)
        else:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Unknown error occurred during execution.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)

    def routesStartWorker(self):  # method to start the worker thread
        if not self.gf.routes_selectedlayer_source or not self.gf.routes_selectedlayer_target:
            QgsMessageLog.logMessage(
                "Warning! No inputlayer selected. Choose your inputlayers and try again.",
                MESSAGE_CATEGORY, Qgis.Critical)
            self.iface.messageBar().pushMessage(
                "Warning",
                " No sourcelayer or no targetlayer selected. Choose your inputlayers and try again.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
            return
        if self.gf.routes_selectedlayer_source.fields().count(
        ) == 0 or self.gf.routes_selectedlayer_target.fields().count() == 0:
            QgsMessageLog.logMessage(
                "Warning! Inputlayer has no fields. Script wont work until you add at least one dummy ID-Field.",
                MESSAGE_CATEGORY, Qgis.Critical)
            self.iface.messageBar().pushMessage(
                "Warning",
                " Inputlayer has no fields - Add at least a dummy-id field.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
            return
        if self.gf.routes_selectedlayer_source.featureCount(
        ) == 0 or self.gf.routes_selectedlayer_target.featureCount() == 0:
            QgsMessageLog.logMessage(
                "Warning! One or both inputlayers are empty. Add some features and try again.",
                MESSAGE_CATEGORY, Qgis.Critical)
            self.iface.messageBar().pushMessage(
                "Warning",
                " One or both inputlayers are empty. Add some features and try again.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
            return
        routes_memorylayer_vl = QgsVectorLayer(
            "LineString?crs=epsg:4326", "Routes",
            "memory")  # Create temporary polygon layer (output file)
        self.routes_thread = QThread()
        self.routes_worker = OpenTripPlannerPluginRoutesWorker(
            self.dlg, self.iface, self.gf, routes_memorylayer_vl)
        # see https://realpython.com/python-pyqt-qthread/#using-qthread-to-prevent-freezing-guis
        # and https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html
        self.routes_worker.moveToThread(
            self.routes_thread)  # move Worker-Class to a thread
        # Connect signals and slots:
        self.routes_thread.started.connect(self.routes_worker.run)
        self.routes_worker.routes_finished.connect(self.routes_thread.quit)
        self.routes_worker.routes_finished.connect(
            self.routes_worker.deleteLater)
        self.routes_thread.finished.connect(self.routes_thread.deleteLater)
        self.routes_worker.routes_progress.connect(self.routesReportProgress)
        self.routes_worker.routes_finished.connect(self.routesFinished)
        self.routes_thread.start()  # finally start the thread
        # Disable/Enable GUI elements to prevent them from beeing used while worker threads are running and accidentially changing settings during progress
        self.gf.disableRoutesGui()
        self.gf.disableGeneralSettingsGui()
        self.routes_thread.finished.connect(lambda: self.gf.enableRoutesGui())
        self.routes_thread.finished.connect(
            lambda: self.gf.enableGeneralSettingsGui())

    def routesKillWorker(self):  # method to kill/cancel the worker thread
        # print('pushed cancel') # debugging
        # see https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html
        try:  # to prevent a Python error when the cancel button has been clicked but no thread is running use try/except
            self.routes_worker.stop(
            )  # call the stop method in worker class to break the work-loop so we can quit the thread
            if self.routes_thread.isRunning():  # check if a thread is running
                # print('pushed cancel, thread is running, trying to cancel') # debugging
                self.routes_thread.requestInterruption()
                self.routes_thread.exit(
                )  # Tells the thread’s event loop to exit with a return code.
                self.routes_thread.quit(
                )  # Tells the thread’s event loop to exit with return code 0 (success). Equivalent to calling exit (0).
                self.routes_thread.wait(
                )  # Blocks the thread until https://doc.qt.io/qtforpython/PySide6/QtCore/QThread.html#PySide6.QtCore.PySide6.QtCore.QThread.wait
        except:
            self.dlg.Routes_ProgressBar.setValue(0)
            self.dlg.Routes_StatusBox.setText('')

    def routesReportProgress(self, progress,
                             status):  # method to report the progress to gui
        self.dlg.Routes_ProgressBar.setValue(
            progress)  # set the current progress in progress bar
        self.dlg.Routes_StatusBox.setText(status)

    def routesFinished(
        self,
        routes_resultlayer,
        routes_state,
        unique_errors="",
        runtime="00:00:00 (unknown)"
    ):  # method to interact with gui when thread is finished or canceled
        QgsProject.instance().addMapLayer(
            routes_resultlayer)  # Show resultlayer in project
        # routes_state is indicating different states of the thread/result as integer
        if unique_errors:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Errors occurred. Check the resultlayer for details. The errors were: "
                + unique_errors +
                " - Created dummy geometries at coordinate 0,0 on error features",
                MESSAGE_CATEGORY,
                level=Qgis.Warning,
                duration=12)
            QgsMessageLog.logMessage(
                "Errors occurred. Check the resultlayer for details. The errors were: "
                + unique_errors +
                " - Created dummy geometries at coordinate 0,0 on error features",
                MESSAGE_CATEGORY, Qgis.Warning)
        if routes_state == 0:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Run-Method was never executed.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)
        elif routes_state == 1:
            self.iface.messageBar().pushMessage("Done!",
                                                " Routes job finished after " +
                                                runtime,
                                                MESSAGE_CATEGORY,
                                                level=Qgis.Success,
                                                duration=6)
        elif routes_state == 2:
            self.iface.messageBar().pushMessage("Done!",
                                                " Routes job canceled after " +
                                                runtime,
                                                MESSAGE_CATEGORY,
                                                level=Qgis.Success,
                                                duration=6)
        elif routes_state == 3:
            self.iface.messageBar().pushMessage(
                "Warning",
                " No Routes to create / no matching attributes - Check your settings and retry.",
                MESSAGE_CATEGORY,
                level=Qgis.Warning,
                duration=6)
        elif routes_state == 99:
            self.iface.messageBar().pushMessage(
                "Debugging",
                " Just having some debugging fun :)",
                MESSAGE_CATEGORY,
                level=Qgis.Info,
                duration=6)
        else:
            self.iface.messageBar().pushMessage(
                "Warning",
                " Unknown error occurred during execution.",
                MESSAGE_CATEGORY,
                level=Qgis.Critical,
                duration=6)

    def run(self):
        """Run method that performs all the real work"""
        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.first_start = False
            self.dlg = OpenTripPlannerPluginDialog()
            self.gf = OpenTripPlannerPluginGeneralFunctions(
                self.dlg, self.iface)
            # Calling maplayer selection on first startup to load layers into QgsMapLayerComboBox and initialize QgsOverrideButton stuff so selections can be done without actually using the QgsMapLayerComboBox (related to currentIndexChanged.connect(self.isochrones_maplayerselection) below)
            self.gf.routes_maplayerselection()
            self.gf.isochrones_maplayerselection()
            self.gf.aggregated_isochrones_maplayerselection()
            # Execute Main-Functions on Click: Placing them here prevents them from beeing executed multiple times, see https://gis.stackexchange.com/a/137161/107424
            self.dlg.Isochrones_RequestIsochrones.clicked.connect(
                lambda: self.isochronesStartWorker()
            )  #Call the start worker method
            self.dlg.Isochrones_Cancel.clicked.connect(
                lambda: self.isochronesKillWorker())
            self.dlg.AggregatedIsochrones_RequestIsochrones.clicked.connect(
                lambda: self.aggregated_isochronesStartWorker()
            )  #Call the start worker method
            self.dlg.AggregatedIsochrones_Cancel.clicked.connect(
                lambda: self.aggregated_isochronesKillWorker())
            self.dlg.Routes_RequestRoutes.clicked.connect(
                lambda: self.routesStartWorker())
            self.dlg.Routes_Cancel.clicked.connect(
                lambda: self.routesKillWorker())

            # Calling Functions on button click
            self.dlg.GeneralSettings_CheckServerStatus.clicked.connect(
                self.gf.check_server_status)
            self.dlg.GeneralSettings_Save.clicked.connect(
                self.gf.store_general_variables
            )  #Call store_general_variables function when clicking on save button
            self.dlg.GeneralSettings_Restore.clicked.connect(
                self.gf.restore_general_variables)
            self.dlg.Isochrones_SaveSettings.clicked.connect(
                self.gf.store_isochrone_variables)
            self.dlg.Isochrones_RestoreDefaultSettings.clicked.connect(
                self.gf.restore_isochrone_variables)
            self.dlg.Isochrones_Now.clicked.connect(
                self.gf.set_datetime_now_isochrone)
            self.dlg.AggregatedIsochrones_SaveSettings.clicked.connect(
                self.gf.store_aggregated_isochrone_variables)
            self.dlg.AggregatedIsochrones_RestoreDefaultSettings.clicked.connect(
                self.gf.restore_aggregated_isochrone_variables)
            self.dlg.AggregatedIsochrones_Now.clicked.connect(
                self.gf.set_datetime_now_aggregated_isochrone)
            self.dlg.Routes_SaveSettings.clicked.connect(
                self.gf.store_route_variables)
            self.dlg.Routes_RestoreDefaultSettings.clicked.connect(
                self.gf.restore_route_variables)
            self.dlg.Routes_Now.clicked.connect(self.gf.set_datetime_now_route)

            # Calling Functions to update layer stuff when layerselection has changed
            self.dlg.Isochrones_SelectInputLayer.currentIndexChanged.connect(
                self.gf.isochrones_maplayerselection
            )  # Call function isochrones_maplayerselection to update all selection related stuff when selection has been changed
            self.dlg.AggregatedIsochrones_SelectInputLayer.currentIndexChanged.connect(
                self.gf.aggregated_isochrones_maplayerselection)
            self.dlg.Routes_SelectInputLayer_Source.currentIndexChanged.connect(
                self.gf.routes_maplayerselection)
            self.dlg.Routes_SelectInputLayer_Target.currentIndexChanged.connect(
                self.gf.routes_maplayerselection)
            self.dlg.Routes_SelectInputField_Source.currentIndexChanged.connect(
                self.gf.routes_maplayerselection)  # or "fieldChanged"?
            self.dlg.Routes_SelectInputField_Target.currentIndexChanged.connect(
                self.gf.routes_maplayerselection)
            self.dlg.Routes_DataDefinedLayer_Source.stateChanged.connect(
                self.gf.routes_maplayerselection)
            self.dlg.Routes_DataDefinedLayer_Target.stateChanged.connect(
                self.gf.routes_maplayerselection)

        # Setting GUI stuff for startup every time the plugin is opened
        self.dlg.Isochrones_Date.setDateTime(
            QtCore.QDateTime.currentDateTime()
        )  # Set Dateselection to today on restart or firststart, only functional if never used save settings, otherwise overwritten by read_route_variables()
        self.dlg.AggregatedIsochrones_FromDateTime.setDateTime(
            QtCore.QDateTime.currentDateTime())
        self.dlg.AggregatedIsochrones_ToDateTime.setDateTime(
            QtCore.QDateTime.currentDateTime())
        self.dlg.Routes_Date.setDateTime(QtCore.QDateTime.currentDateTime())
        self.dlg.Isochrones_ProgressBar.setValue(
            0)  # Set Progressbar to 0 on restart or first start
        self.dlg.AggregatedIsochrones_ProgressBar.setValue(0)
        self.dlg.Routes_ProgressBar.setValue(0)
        self.dlg.GeneralSettings_ServerStatusResult.setText(
            "Serverstatus Unknown")
        self.dlg.GeneralSettings_ServerStatusResult.setStyleSheet(
            "background-color: white; color: black ")

        # Functions to execute every time the plugin is opened
        self.gf.read_general_variables(
        )  #Run Read-Stored-Variables-Function on every start
        self.gf.read_isochrone_variables()
        self.gf.read_aggregated_isochrone_variables()
        self.gf.read_route_variables()

        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            QgsMessageLog.logMessage(
                "OpenTripPlanner Plugin is already running! Close it before, if you wish to restart it.",
                MESSAGE_CATEGORY, Qgis.Warning)
            self.iface.messageBar().pushMessage(
                "Error",
                "OpenTripPlanner Plugin is already running! Close it before, if you wish to restart it.",
                MESSAGE_CATEGORY,
                level=Qgis.Warning,
                duration=6)