From d6b28aef5cfe8f610b15b1a565bf8787c242dc60 Mon Sep 17 00:00:00 2001 From: Faisal Shahzad <84210709+seowings@users.noreply.github.com> Date: Fri, 22 Dec 2023 22:17:57 +0100 Subject: [PATCH] included ipython widget via qtconsole --- .gitignore | 1 + setup.py | 4 +- src/staticwordpress/gui/__init__.py | 2 +- src/staticwordpress/gui/config.py | 8 +- src/staticwordpress/gui/editor.py | 61 ++++++++ src/staticwordpress/gui/logger.py | 2 +- .../gui/{mainwindow.py => main.py} | 132 +++++++++++------- src/staticwordpress/gui/project.py | 10 +- src/staticwordpress/gui/rawtext.py | 4 +- src/staticwordpress/gui/workflow.py | 2 +- src/staticwordpress/share/gui.json | 13 ++ src/staticwordpress/share/icons/python.svg | 38 +++++ 12 files changed, 207 insertions(+), 70 deletions(-) create mode 100644 src/staticwordpress/gui/editor.py rename src/staticwordpress/gui/{mainwindow.py => main.py} (89%) create mode 100644 src/staticwordpress/share/icons/python.svg diff --git a/.gitignore b/.gitignore index 981373b..bb4075c 100644 --- a/.gitignore +++ b/.gitignore @@ -124,6 +124,7 @@ celerybeat.pid # Environments .env .venv +.venv10 env/ venv/ ENV/ diff --git a/setup.py b/setup.py index b570531..758cb9d 100644 --- a/setup.py +++ b/setup.py @@ -73,9 +73,7 @@ extras_require = { "mkdocstrings[python]", "pymdown-extensions", ], - "gui": [ - "pyqt5", - ], + "gui": ["pyqt5", "qtconsole"], } extras_require["all"] = list( {rq for target in extras_require.keys() for rq in extras_require[target]} diff --git a/src/staticwordpress/gui/__init__.py b/src/staticwordpress/gui/__init__.py index ee98d5b..096bdd6 100644 --- a/src/staticwordpress/gui/__init__.py +++ b/src/staticwordpress/gui/__init__.py @@ -27,4 +27,4 @@ specific language governing rights and limitations under the License. # INTERNAL IMPORTS # +++++++++++++++++++++++++++++++++++++++++++++++++++++ -from .mainwindow import main +from .main import main diff --git a/src/staticwordpress/gui/config.py b/src/staticwordpress/gui/config.py index a49cc41..6aa2852 100644 --- a/src/staticwordpress/gui/config.py +++ b/src/staticwordpress/gui/config.py @@ -69,7 +69,7 @@ from ..core.constants import ( # +++++++++++++++++++++++++++++++++++++++++++++++++++++ -class ConfigTabBar(QTabBar): +class SWConfigTabBar(QTabBar): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) return @@ -102,15 +102,15 @@ class ConfigTabBar(QTabBar): return -class ConfigDialog(QDialog): +class SWConfigDialog(QDialog): def __init__(self, parent=None): - super(ConfigDialog, self).__init__(parent=parent) + super(SWConfigDialog, self).__init__(parent=parent) self.app_configurations = QSettings( CONFIGS["APPLICATION_NAME"], CONFIGS["APPLICATION_NAME"] ) self.tabswidget_configs = QTabWidget() - self.tabswidget_configs.setTabBar(ConfigTabBar()) + self.tabswidget_configs.setTabBar(SWConfigTabBar()) self.tabswidget_configs.setTabPosition(QTabWidget.West) self.tab_general = QWidget() diff --git a/src/staticwordpress/gui/editor.py b/src/staticwordpress/gui/editor.py new file mode 100644 index 0000000..9a55246 --- /dev/null +++ b/src/staticwordpress/gui/editor.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +""" +STATIC WORDPRESS: WordPress as Static Site Generator +A Python Package for Converting WordPress Installation to a Static Website +https://github.com/serpwings/staticwordpress + + src\staticwordpress\gui\editor.py + + Copyright (C) 2020-2023 Faisal Shahzad + + +The contents of this file are subject to version 3 of the +GNU General Public License (GPL-3.0). You may not use this file except in +compliance with the License. You may obtain a copy of the License at +https://www.gnu.org/licenses/gpl-3.0.txt +https://github.com/serpwings/staticwordpress/blob/master/LICENSE + + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +""" + + +# +++++++++++++++++++++++++++++++++++++++++++++++++++++ +# 3rd PARTY LIBRARY IMPORTS +# +++++++++++++++++++++++++++++++++++++++++++++++++++++ + +from qtconsole.rich_jupyter_widget import RichJupyterWidget +from qtconsole.inprocess import QtInProcessKernelManager +from IPython.lib import guisupport + + +# +++++++++++++++++++++++++++++++++++++++++++++++++++++ +# IMPLEMENATIONS +# +++++++++++++++++++++++++++++++++++++++++++++++++++++ + + +class SWIPythonWidget(RichJupyterWidget): + def __init__(self, interface_: dict = {"iface": None}, *args, **kwargs): + super(SWIPythonWidget, self).__init__(*args, **kwargs) + + self.ipython_kernal_manager = QtInProcessKernelManager() + self.ipython_kernal_manager.start_kernel() + self.kernel_client = self.ipython_kernal_manager.client() + self.kernel_client.start_channels() + + import_custom_modules = ["import requests"] + for module in import_custom_modules: + self._execute(module, hidden=True) + + self.ipython_kernal_manager.kernel.shell.push(interface_) + + def stop(): + self.kernel_client.stop_channels() + self.ipython_kernal_manager.shutdown_kernel() + guisupport.get_app_qt4().exit() + + self.exit_requested.connect(stop) diff --git a/src/staticwordpress/gui/logger.py b/src/staticwordpress/gui/logger.py index 69d34ae..f132af1 100644 --- a/src/staticwordpress/gui/logger.py +++ b/src/staticwordpress/gui/logger.py @@ -41,7 +41,7 @@ from PyQt5.QtCore import QObject, pyqtSignal # +++++++++++++++++++++++++++++++++++++++++++++++++++++ -class LoggerWidget(logging.Handler, QObject): +class SWLoggerWidget(logging.Handler, QObject): append_text_message = pyqtSignal(str) def __init__(self, parent): diff --git a/src/staticwordpress/gui/mainwindow.py b/src/staticwordpress/gui/main.py similarity index 89% rename from src/staticwordpress/gui/mainwindow.py rename to src/staticwordpress/gui/main.py index 08b812e..130fa3b 100644 --- a/src/staticwordpress/gui/mainwindow.py +++ b/src/staticwordpress/gui/main.py @@ -48,6 +48,7 @@ from PyQt5.QtWidgets import ( QProgressBar, QMenu, QToolBar, + QDockWidget, ) from PyQt5.QtGui import QIcon, QDesktopServices from PyQt5.QtCore import Qt, QThread, QSize, QSettings, QUrl @@ -69,19 +70,20 @@ from ..core.utils import ( get_remote_content, extract_urls_from_raw_text, ) -from ..gui.workflow import WorkflowGUI -from ..gui.logger import LoggerWidget -from ..gui.rawtext import RawTextDialog -from ..gui.config import ConfigDialog -from ..gui.project import ProjectDialog -from ..gui.utils import GUI_SETTINGS, logging_decorator +from .workflow import SWWorkflowObject +from .logger import SWLoggerWidget +from .editor import SWIPythonWidget +from .rawtext import SWRawTextDialog +from .config import SWConfigDialog +from .project import SWProjectDialog +from .utils import GUI_SETTINGS, logging_decorator # +++++++++++++++++++++++++++++++++++++++++++++++++++++ # IMPLEMENATIONS # +++++++++++++++++++++++++++++++++++++++++++++++++++++ -class StaticWordPressGUI(QMainWindow): +class SWMainWindow(QMainWindow): def __init__(self): super().__init__() self.app_configurations = QSettings( @@ -90,16 +92,16 @@ class StaticWordPressGUI(QMainWindow): self._project = Project() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() - self.text_edit_logging = LoggerWidget(self) + self.text_edit_logging = SWLoggerWidget(self) self.text_edit_logging.setFormatter( logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") ) logging.getLogger().addHandler(self.text_edit_logging) logging.getLogger().setLevel(logging.INFO) - self.setCentralWidget(self.text_edit_logging.plaintext_edit) + self.statusBar().showMessage(f"{CONFIGS['APPLICATION_NAME']} is Ready") self.progress_bar = QProgressBar() self.progress_bar.setAlignment(Qt.AlignCenter) @@ -159,6 +161,13 @@ class StaticWordPressGUI(QMainWindow): if current_toolbar: current_toolbar.addAction(action) + # docked widgets + self.dockwidget_ipython = QDockWidget("IPython Console", self) + self.dockwidget_ipython.setFloating(False) + self.dockwidget_ipython.hide() + self.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget_ipython) + self.ipython_console = None + self.setWindowIcon(QIcon(f"{SHARE_FOLDER_PATH}/icons/static-wordpress.svg")) self.setWindowTitle(f"{CONFIGS['APPLICATION_NAME']} Version - {VERISON}") self.setMinimumSize(QSize(1366, 768)) @@ -229,14 +238,16 @@ class StaticWordPressGUI(QMainWindow): @is_new_project @logging_decorator def extract_url_from_raw_text(self): - rtp = RawTextDialog( + raw_text_dialog = SWRawTextDialog( parent=self, src_url=self._project.src_url, dest_url=self._project.dst_url ) - if rtp.exec_(): - raw_text = rtp.textedit_raw_text_with_links.toPlainText() + if raw_text_dialog.exec_(): + raw_text = raw_text_dialog.textedit_raw_text_with_links.toPlainText() if raw_text: new_additional_links = extract_urls_from_raw_text( - raw_text, rtp.lineedit_dest_url.text(), rtp.linedit_src_url.text() + raw_text, + raw_text_dialog.lineedit_dest_url.text(), + raw_text_dialog.linedit_src_url.text(), ) logging.info(f" {len(new_additional_links)} Additional Urls Found") self._project.additional += new_additional_links @@ -251,31 +262,33 @@ class StaticWordPressGUI(QMainWindow): def closeEvent(self, event): """ """ - msgBox = QMessageBox(parent=self) - msgBox.setWindowTitle(f"Exiting {CONFIGS['APPLICATION_NAME']}") - msgBox.setIcon(QMessageBox.Question) - msgBox.setText( + message_box = QMessageBox(parent=self) + message_box.setWindowTitle(f"Exiting {CONFIGS['APPLICATION_NAME']}") + message_box.setIcon(QMessageBox.Question) + message_box.setText( "Do you really want to exit?.
Any unsaved changes will be lost!", ) - pushbuttonOk = msgBox.addButton("OK", QMessageBox.YesRole) + pushbuttonOk = message_box.addButton("OK", QMessageBox.YesRole) pushbuttonOk.setIcon(QIcon(f"{SHARE_FOLDER_PATH}/icons/ok.svg")) - pushbuttonNo = msgBox.addButton("Cancel", QMessageBox.NoRole) + pushbuttonNo = message_box.addButton("Cancel", QMessageBox.NoRole) pushbuttonNo.setIcon(QIcon(f"{SHARE_FOLDER_PATH}/icons/cancel.svg")) - msgBox.setDefaultButton(pushbuttonOk) + message_box.setDefaultButton(pushbuttonOk) - msgBox.setWindowIcon(QIcon(f"{SHARE_FOLDER_PATH}/icons/static-wordpress.svg")) - msgBox.setTextFormat(Qt.RichText) - msgBox.exec_() + message_box.setWindowIcon( + QIcon(f"{SHARE_FOLDER_PATH}/icons/static-wordpress.svg") + ) + message_box.setTextFormat(Qt.RichText) + message_box.exec_() - if msgBox.clickedButton() == pushbuttonOk: + if message_box.clickedButton() == pushbuttonOk: if self._bg_thread.isRunning(): self._bg_thread.quit() del self._bg_thread del self._bg_worker - super(StaticWordPressGUI, self).closeEvent(event) + super(SWMainWindow, self).closeEvent(event) event.accept() @@ -313,38 +326,51 @@ class StaticWordPressGUI(QMainWindow): if not QDesktopServices.openUrl(url): QMessageBox.warning(self, "Open Help URL", "Could not open Help URL") + def startIPython(self): + """ """ + if self.findChild(QAction, "action_ipython_widget").isChecked(): + if self.ipython_console is None: + self.ipython_console = SWIPythonWidget(interface_={"iface": self}) + self.dockwidget_ipython.setWidget(self.ipython_console) + + self.dockwidget_ipython.show() + else: + self.dockwidget_ipython.hide() + @logging_decorator def show_configs(self): """Interface with System Configurations""" - w = ConfigDialog(parent=self) - if w.exec_(): + config_dialog = SWConfigDialog(parent=self) + if config_dialog.exec_(): logging.info("Saved/Updated Default Configurations") def about(self): """ """ - msgBox = QMessageBox(parent=self) - msgBox.setText( + message_box = QMessageBox(parent=self) + message_box.setText( f"Copyright {date.today().year} - SERP Wings" f"

{CONFIGS['APPLICATION_NAME']} Version - {VERISON}" "

This work is an opensource project under
GNU General Public License v3 or later (GPLv3+)" f"
More Information at {CONFIGS['ORGANIZATION_NAME']}" ) - msgBox.addButton(QMessageBox.Ok).setIcon( + message_box.addButton(QMessageBox.Ok).setIcon( QIcon(f"{SHARE_FOLDER_PATH}/icons/ok.svg") ) - msgBox.setWindowIcon(QIcon(f"{SHARE_FOLDER_PATH}/icons/static-wordpress.svg")) - msgBox.setTextFormat(Qt.RichText) - msgBox.setWindowTitle("About Us") - msgBox.exec() + message_box.setWindowIcon( + QIcon(f"{SHARE_FOLDER_PATH}/icons/static-wordpress.svg") + ) + message_box.setTextFormat(Qt.RichText) + message_box.setWindowTitle("About Us") + message_box.exec() @logging_decorator def new_project(self): """Closing current project will automatically start a new project.""" self.close_project() - proj_dialog = ProjectDialog(self, self._project, title_="New Project") - if proj_dialog.exec_(): - self._project = proj_dialog._project + project_dialog = SWProjectDialog(self, self._project, title_="New Project") + if project_dialog.exec_(): + self._project = project_dialog._project self.app_configurations.setValue("last-project", self._project.output) if Path(self._project.output).is_dir(): @@ -377,7 +403,7 @@ class StaticWordPressGUI(QMainWindow): if project_path.exists(): self._project.open(project_path) if self._project.is_open(): - project_dialog = ProjectDialog( + project_dialog = SWProjectDialog( parent=self, project_=self._project, title_="Project Properties" ) @@ -418,7 +444,7 @@ class StaticWordPressGUI(QMainWindow): def show_project(self): """showing static-wordpress Project File""" if self._project.is_open(): - project_dialog = ProjectDialog( + project_dialog = SWProjectDialog( self, self._project, title_="Current Project" ) if project_dialog.exec_(): @@ -500,7 +526,7 @@ class StaticWordPressGUI(QMainWindow): if self._bg_thread.isRunning(): self._bg_thread.quit() - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) if self._project.src_type == SOURCE.ZIP: @@ -569,7 +595,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -583,7 +609,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -597,7 +623,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -611,7 +637,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -625,7 +651,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -639,7 +665,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -654,7 +680,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -689,7 +715,7 @@ class StaticWordPressGUI(QMainWindow): if self._bg_thread.isRunning(): self._bg_thread.quit() - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) @@ -705,7 +731,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -720,7 +746,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -735,7 +761,7 @@ class StaticWordPressGUI(QMainWindow): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) @@ -791,7 +817,7 @@ class StaticWordPressGUI(QMainWindow): def main(): app = QApplication(sys.argv) - wind = StaticWordPressGUI() + wind = SWMainWindow() sys.exit(app.exec_()) diff --git a/src/staticwordpress/gui/project.py b/src/staticwordpress/gui/project.py index 1f9f157..b5b6dc1 100644 --- a/src/staticwordpress/gui/project.py +++ b/src/staticwordpress/gui/project.py @@ -72,23 +72,23 @@ from ..core.constants import ( from ..core.project import Project from ..core.utils import is_url_valid -from ..gui.workflow import WorkflowGUI +from ..gui.workflow import SWWorkflowObject # +++++++++++++++++++++++++++++++++++++++++++++++++++++ # IMPLEMENATIONS # +++++++++++++++++++++++++++++++++++++++++++++++++++++ -class ProjectDialog(QDialog): +class SWProjectDialog(QDialog): def __init__(self, parent, project_, title_="Project Settings"): - super(ProjectDialog, self).__init__(parent=parent) + super(SWProjectDialog, self).__init__(parent=parent) self.appConfigurations = QSettings( CONFIGS["APPLICATION_NAME"], CONFIGS["APPLICATION_NAME"] ) self._project = project_ self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() vertical_layout_project = QVBoxLayout() groupbox_general_settings = QGroupBox("General Settings") @@ -390,7 +390,7 @@ class ProjectDialog(QDialog): self._bg_thread.quit() self._bg_thread = QThread(parent=self) - self._bg_worker = WorkflowGUI() + self._bg_worker = SWWorkflowObject() self._bg_worker.set_project(project_=self._project) self._bg_worker.moveToThread(self._bg_thread) self._bg_thread.finished.connect(self._bg_worker.deleteLater) diff --git a/src/staticwordpress/gui/rawtext.py b/src/staticwordpress/gui/rawtext.py index 6f422d9..184a6db 100644 --- a/src/staticwordpress/gui/rawtext.py +++ b/src/staticwordpress/gui/rawtext.py @@ -51,9 +51,9 @@ from ..core.constants import CONFIGS, SHARE_FOLDER_PATH # +++++++++++++++++++++++++++++++++++++++++++++++++++++ -class RawTextDialog(QDialog): +class SWRawTextDialog(QDialog): def __init__(self, parent, src_url: str, dest_url: str): - super(RawTextDialog, self).__init__(parent=parent) + super(SWRawTextDialog, self).__init__(parent=parent) self.appConfigurations = QSettings( CONFIGS["APPLICATION_NAME"], CONFIGS["APPLICATION_NAME"] ) diff --git a/src/staticwordpress/gui/workflow.py b/src/staticwordpress/gui/workflow.py index 33b6f2a..80c4a93 100644 --- a/src/staticwordpress/gui/workflow.py +++ b/src/staticwordpress/gui/workflow.py @@ -44,7 +44,7 @@ from ..gui.utils import logging_decorator, progress_decorator # +++++++++++++++++++++++++++++++++++++++++++++++++++++ -class WorkflowGUI(QObject): +class SWWorkflowObject(QObject): emit_sitemap_location = pyqtSignal(str) emit_progress = pyqtSignal(str, int) emit_verification = pyqtSignal(dict) diff --git a/src/staticwordpress/share/gui.json b/src/staticwordpress/share/gui.json index 28c5c22..0b889e6 100644 --- a/src/staticwordpress/share/gui.json +++ b/src/staticwordpress/share/gui.json @@ -164,6 +164,19 @@ "seperator": false, "toolbar": "toolbar_edit" }, + { + "icon": "/icons/python.svg", + "name": "action_ipython_widget", + "visible": true, + "text": "&IPython", + "shortcut": "F10", + "tooltip": "IPython (F10)", + "function": "self.startIPython", + "setCheckable": true, + "menu": "menu_edit", + "seperator": false, + "toolbar": "toolbar_edit" + }, { "icon": "/icons/configs.svg", "name": "action_file_project_close", diff --git a/src/staticwordpress/share/icons/python.svg b/src/staticwordpress/share/icons/python.svg new file mode 100644 index 0000000..71e7688 --- /dev/null +++ b/src/staticwordpress/share/icons/python.svg @@ -0,0 +1,38 @@ + + + + + +