Started implementing the "flask routes" function.

Thu, 12 Nov 2020 19:43:14 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Thu, 12 Nov 2020 19:43:14 +0100
changeset 7
a140b2a8ba93
parent 6
d491ccab7343
child 8
cfbd3a2757fd

Started implementing the "flask routes" function.

PluginFlask.e4p file | annotate | diff | comparison | revisions
ProjectFlask/Project.py file | annotate | diff | comparison | revisions
ProjectFlask/RoutesDialog.py file | annotate | diff | comparison | revisions
ProjectFlask/RoutesDialog.ui file | annotate | diff | comparison | revisions
--- a/PluginFlask.e4p	Wed Nov 11 20:03:21 2020 +0100
+++ b/PluginFlask.e4p	Thu Nov 12 19:43:14 2020 +0100
@@ -19,12 +19,14 @@
     <Source>ProjectFlask/ConfigurationPage/FlaskPage.py</Source>
     <Source>ProjectFlask/ConfigurationPage/__init__.py</Source>
     <Source>ProjectFlask/Project.py</Source>
+    <Source>ProjectFlask/RoutesDialog.py</Source>
     <Source>ProjectFlask/RunServerDialog.py</Source>
     <Source>ProjectFlask/__init__.py</Source>
     <Source>__init__.py</Source>
   </Sources>
   <Forms>
     <Form>ProjectFlask/ConfigurationPage/FlaskPage.ui</Form>
+    <Form>ProjectFlask/RoutesDialog.ui</Form>
     <Form>ProjectFlask/RunServerDialog.ui</Form>
   </Forms>
   <Others>
--- a/ProjectFlask/Project.py	Wed Nov 11 20:03:21 2020 +0100
+++ b/ProjectFlask/Project.py	Thu Nov 12 19:43:14 2020 +0100
@@ -10,7 +10,7 @@
 import os
 
 from PyQt5.QtCore import (
-    pyqtSlot, QObject, QProcess, QProcessEnvironment, QTimer
+    pyqtSlot, QObject, QProcess, QProcessEnvironment
 )
 from PyQt5.QtWidgets import QMenu
 
@@ -24,6 +24,7 @@
 import Utilities
 
 from .RunServerDialog import RunServerDialog
+from .RoutesDialog import RoutesDialog
 
 
 class Project(QObject):
@@ -54,6 +55,7 @@
          
 ##        self.__serverProc = None
         self.__serverDialog = None
+        self.__routesDialog = None
        
         self.__flaskVersions = {
             "python": "",
@@ -99,6 +101,24 @@
         self.runDevServerAct.triggered.connect(self.__runDevelopmentServer)
         self.actions.append(self.runDevServerAct)
         
+        ##############################
+        ## routes action below        ##
+        ##############################
+        
+        self.showRoutesAct = E5Action(
+            self.tr('Show Routes'),
+            self.tr('Show &Routes'),
+            0, 0,
+            self, 'flask_show_routes')
+        self.showRoutesAct.setStatusTip(self.tr(
+            'Shows a dialog with the routes of the flask app'))
+        self.showRoutesAct.setWhatsThis(self.tr(
+            """<b>Show Routes</b>"""
+            """<p>Shows a dialog with the routes of the flask app.</p>"""
+        ))
+        self.showRoutesAct.triggered.connect(self.__showRoutes)
+        self.actions.append(self.showRoutesAct)
+        
         ##################################
         ## documentation action below   ##
         ##################################
@@ -150,6 +170,8 @@
         menu.addAction(self.runServerAct)
         menu.addAction(self.runDevServerAct)
         menu.addSeparator()
+        menu.addAction(self.showRoutesAct)
+        menu.addSeparator()
         menu.addAction(self.documentationAct)
         menu.addSeparator()
         menu.addAction(self.aboutFlaskAct)
@@ -454,3 +476,10 @@
         Private slot showing all URL dispatch routes.
         """
         # TODO: implement this (flask routes)
+        if self.__routesDialog is not None:
+            self.__routesDialog.close()
+        
+        dlg = RoutesDialog()
+        if dlg.showRoutes(self):
+            dlg.show()
+            self.__routesDialog = dlg
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ProjectFlask/RoutesDialog.py	Thu Nov 12 19:43:14 2020 +0100
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show the application routes.
+"""
+
+from PyQt5.QtCore import pyqtSlot, QProcess
+from PyQt5.QtWidgets import QDialog
+
+from E5Gui import E5MessageBox
+
+from .Ui_RoutesDialog import Ui_RoutesDialog
+
+
+class RoutesDialog(QDialog, Ui_RoutesDialog):
+    """
+    Class implementing a dialog to show the application routes.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(RoutesDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__process = None
+    
+    def showRoutes(self, project):
+        """
+        Public method to show the list of routes.
+        
+        @param project reference to the project object
+        @type Project
+        @return flag indicating success
+        @rtype bool
+        """
+        workdir, env = project.prepareRuntimeEnvironment()
+        if env is not None:
+            command = project.getFlaskCommand()
+            
+            self.__process = QProcess()
+            self.__process.setProcessEnvironment(env)
+            self.__process.setWorkingDirectory(workdir)
+            self.__process.setProcessChannelMode(QProcess.MergedChannels)
+            
+            args = ["routes"]
+            if self.matchButton.isChecked():
+                sortorder = "match"
+            elif self.endpointButton.isChecked():
+                sortorder = "endpoint"
+            elif self.methodsButton.isChecked():
+                sortorder = "methods"
+            elif self.ruleButton.isChecked():
+                sortorder = "rule"
+            else:
+                sortorder = ""
+            if sortorder:
+                args += ["--sort", sortorder]
+            if self.allMethodsCheckBox.isChecked():
+                args.append("--all-methods")
+            
+            self.__process.start(command, args)
+            ok = self.__process.waitForStarted(10000)
+            if ok:
+                ok = self.__process.waitForFinished(10000)
+                if ok:
+                    out = str(self.__process.readAllStandardOutput(), "utf-8")
+                    self.__processOutput(out)
+                else:
+                    E5MessageBox.critical(
+                        None,
+                        self.tr("Flask Routes"),
+                        self.tr("""The Flask process did not finish within"""
+                                """ 10 seconds."""))
+            else:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("Run Flask Server"),
+                    self.tr("""The Flask process could not be started."""))
+            return ok
+        else:
+            return False
+    
+    def __processOutput(self, output):
+        """
+        Private method to process the flask output and populate the routes
+        list.
+        
+        @param output output of the flask process
+        @type str
+        """
+        self.routesList.clear()
+        
+        # split output into lines
+        # determine field width based on second line
+        # split each line based on widths and populate list
+        print(output)
+    
+    @pyqtSlot(bool)
+    def on_matchButton_toggled(self, checked):
+        """
+        Slot documentation goes here.
+        
+        @param checked DESCRIPTION
+        @type bool
+        """
+        # TODO: not implemented yet
+        pass
+    
+    @pyqtSlot(bool)
+    def on_endpointButton_toggled(self, checked):
+        """
+        Slot documentation goes here.
+        
+        @param checked DESCRIPTION
+        @type bool
+        """
+        # TODO: not implemented yet
+        pass
+    
+    @pyqtSlot(bool)
+    def on_methodsButton_toggled(self, checked):
+        """
+        Slot documentation goes here.
+        
+        @param checked DESCRIPTION
+        @type bool
+        """
+        # TODO: not implemented yet
+        pass
+    
+    @pyqtSlot(bool)
+    def on_ruleButton_toggled(self, checked):
+        """
+        Slot documentation goes here.
+        
+        @param checked DESCRIPTION
+        @type bool
+        """
+        # TODO: not implemented yet
+        pass
+    
+    @pyqtSlot(bool)
+    def on_allMethodsCheckBox_toggled(self, checked):
+        """
+        Slot documentation goes here.
+        
+        @param checked DESCRIPTION
+        @type bool
+        """
+        # TODO: not implemented yet
+        pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ProjectFlask/RoutesDialog.ui	Thu Nov 12 19:43:14 2020 +0100
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RoutesDialog</class>
+ <widget class="QDialog" name="RoutesDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Flask Routes</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Sort Method</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QRadioButton" name="matchButton">
+        <property name="toolTip">
+         <string>Select to sort by match order</string>
+        </property>
+        <property name="text">
+         <string>match</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="endpointButton">
+        <property name="toolTip">
+         <string>Select to sort by endpoint name</string>
+        </property>
+        <property name="text">
+         <string>Endpoint</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="methodsButton">
+        <property name="toolTip">
+         <string>Select to sort by method name</string>
+        </property>
+        <property name="text">
+         <string>Methods</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="ruleButton">
+        <property name="toolTip">
+         <string>Select to sort by rule</string>
+        </property>
+        <property name="text">
+         <string>Rule</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="allMethodsCheckBox">
+     <property name="toolTip">
+      <string>Select to also show HEAD and OPTIONS methods</string>
+     </property>
+     <property name="text">
+      <string>Show all methods</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QTreeWidget" name="routesList">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::NoSelection</enum>
+     </property>
+     <property name="rootIsDecorated">
+      <bool>false</bool>
+     </property>
+     <property name="itemsExpandable">
+      <bool>false</bool>
+     </property>
+     <column>
+      <property name="text">
+       <string>Endpoint</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Methods</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Rule</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>matchButton</tabstop>
+  <tabstop>endpointButton</tabstop>
+  <tabstop>methodsButton</tabstop>
+  <tabstop>ruleButton</tabstop>
+  <tabstop>allMethodsCheckBox</tabstop>
+  <tabstop>routesList</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>RoutesDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>RoutesDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

eric ide

mercurial