diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/debugcontroller.py b/plugins/extensions/pykrita/plugin/plugins/scripter/debugcontroller.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/debugcontroller.py @@ -0,0 +1,63 @@ +from scripter.debugger_scripter import debugger +from code import InteractiveConsole +import asyncio + + +class DebugController (object): + + def __init__(self, scripter): + self._debugger = None + self._cmd = None + self.scripter = scripter + + def start(self, document): + self.setCmd(compile(document.data, document.filePath, "exec")) + self._debugger = debugger.Debugger(self.scripter, self._cmd) + self._debugger.debugprocess.start() + loop = asyncio.get_event_loop() + loop.run_until_complete(self._debugger.start()) + self.updateUIDebugger() + + def step(self): + loop = asyncio.get_event_loop() + loop.run_until_complete(self._debugger.step()) + self.scripter.uicontroller.setStepped(True) + self.updateUIDebugger() + + def stop(self): + loop = asyncio.get_event_loop() + loop.run_until_complete(self._debugger.stop()) + self.updateUIDebugger() + self._debugger = None + + def setCmd(self, cmd): + self._cmd = cmd + + @property + def isActive(self): + try: + if self._debugger: + return self._debugger.debugprocess.is_alive() + return False + except: + return False + + @property + def currentLine(self): + try: + if self._debugger: + return int(self._debugger.application_data['lineNumber']) + except: + return 0 + + def updateUIDebugger(self): + if not self.isActive or self._quitDebugger(): + widget = self.scripter.uicontroller.findStackWidget('Debugger') + widget.disableToolbar(True) + self.scripter.uicontroller.repaintDebugArea() + + def _quitDebugger(self): + try: + return self._debugger.application_data['quit'] + except: + return False diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/__init__.py b/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/__init__.py new file mode 100644 diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/debugger.py b/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/debugger.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/debugger.py @@ -0,0 +1,93 @@ +import bdb +import asyncio +import inspect +import multiprocessing + + +class Debugger(bdb.Bdb): + + def __init__(self, scripter, cmd): + bdb.Bdb.__init__(self) + + self.quit = False + self.debugq = multiprocessing.Queue() + self.scripter = scripter + self.applicationq = multiprocessing.Queue() + + # Create the debug process + self.debugprocess = multiprocessing.Process(target=self.run, args=(cmd,)) + self.application_data = {} + self.currentLine = 0 + # initialize parent + bdb.Bdb.reset(self) + + def user_call(self, frame, args): + name = frame.f_code.co_name or "" + + def user_line(self, frame): + """Handler that executes with every line of code""" + self.setCurrentLine(frame.f_lineno) + self.applicationq.put({ "lineNumber": self.getCurrentLine()}) + + if self.quit: + return self.set_quit() + + if self.getCurrentLine()==0: + return + else: + # Get a reference to the code object and source + co = frame.f_code + source = inspect.getsourcelines(co)[0] + + # Wait for a debug command + cmd = self.debugq.get() + + if cmd == "step": + # If stepping through code, return this handler + return + + if cmd == "stop": + # If stopping execution, raise an exception + return self.set_quit() + + def user_return(self, frame, value): + name = frame.f_code.co_name or "" + if name == '': + self.applicationq.put({ "quit": True}) + + def user_exception(self, frame, exception): + name = frame.f_code.co_name or "" + + def getCurrentLine(self): + return self.currentLine + + def setCurrentLine(self, line): + self.currentLine = line + + async def display(self): + """Coroutine for updating the UI""" + + # Wait for the application queue to have an update to the GUI + while True: + if self.applicationq.empty(): + await asyncio.sleep(0.5) + else: + # The application queue has at least one item, let's act on every item that's in it + while not self.applicationq.empty(): + # Get info to the GUI + self.application_data = self.applicationq.get() + self.scripter.uicontroller.repaintDebugArea() + return + + async def start(self): + await self.display() + + async def step(self): + # Tell the debugger we want to step in + self.debugq.put("step") + + await self.display() + + async def stop(self): + # Tell the debugger we're stopping execution + self.debugq.put("stop") diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/document_scripter/document.py b/plugins/extensions/pykrita/plugin/plugins/scripter/document_scripter/document.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/document_scripter/document.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/document_scripter/document.py @@ -10,28 +10,20 @@ def open(self, filePath=''): if filePath: self._filePath = filePath - _file = QFile(self._filePath) - if _file.open(QIODevice.ReadOnly | QIODevice.Text): - out = QTextStream(_file) - while not out.atEnd(): - line = out.readLine() - self._document.append(line) - - _file.close() + with open(self._filePath, 'r') as pythonFile: + self._document = pythonFile.read() def save(self): with open(self._filePath, 'w') as pythonFile: - for line in self._document: - print(line, file=pythonFile) + print(self._document, file=pythonFile) def compare(self, new_doc): if len(self._document) != len(new_doc): return False - for line in range(len(new_doc)): - if new_doc[line] != self._document[line]: - return False + if new_doc != self._document: + return False return True diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/documentcontroller.py b/plugins/extensions/pykrita/plugin/plugins/scripter/documentcontroller.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/documentcontroller.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/documentcontroller.py @@ -21,10 +21,10 @@ if not self._activeDocument: self._activeDocument = document.Document(filePath) - dataList = str(data).splitlines() + text = str(data) - if not self._activeDocument.compare(dataList): - self._activeDocument.data = dataList + if not self._activeDocument.compare(text): + self._activeDocument.data = text self._activeDocument.save() return self._activeDocument diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/scripter.py b/plugins/extensions/pykrita/plugin/plugins/scripter/scripter.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/scripter.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/scripter.py @@ -2,8 +2,8 @@ from PyQt5.QtWidgets import * from PyQt5.QtCore import * from krita import * -from scripter import uicontroller -from scripter import documentcontroller +from scripter import uicontroller, documentcontroller, debugcontroller + class ScripterViewExtension(ViewExtension): @@ -18,6 +18,7 @@ def initialize(self): self.uicontroller = uicontroller.UIController(QDialog()) self.documentcontroller = documentcontroller.DocumentController() + self.debugcontroller = debugcontroller.DebugController(self) self.uicontroller.initialize(self) Krita.instance().addViewExtension(ScripterViewExtension(Krita.instance())) diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/__init__.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/__init__.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/__init__.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/__init__.py @@ -2,4 +2,5 @@ 'openaction.openaction.OpenAction', 'saveaction.saveaction.SaveAction', 'runaction.runaction.RunAction', - 'settingsaction.settingsaction.SettingsAction'] + 'settingsaction.settingsaction.SettingsAction', + 'debugaction.debugaction.DebugAction'] diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/debugaction/__init__.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/debugaction/__init__.py new file mode 100644 diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/debugaction/debugaction.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/debugaction/debugaction.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/debugaction/debugaction.py @@ -0,0 +1,26 @@ +from PyQt5.QtWidgets import QAction +from PyQt5.QtGui import QIcon, QPixmap + + +class DebugAction(QAction): + + def __init__(self, scripter, parent=None): + super(DebugAction, self).__init__(parent) + self.scripter = scripter + + self.triggered.connect(self.debug) + + self.setText('Debug') + # path to the icon + self.setIcon(QIcon('/home/eliakincosta/Pictures/debug.svg')) + + @property + def parent(self): + return 'toolBar' + + def debug(self): + if self.scripter.uicontroller.invokeAction('save'): + self.scripter.uicontroller.setActiveWidget('Debugger') + self.scripter.debugcontroller.start(self.scripter.documentcontroller.activeDocument) + widget = self.scripter.uicontroller.findStackWidget('Debugger') + widget.startDebugger() diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/openaction/openaction.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/openaction/openaction.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/openaction/openaction.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/openaction/openaction.py @@ -29,7 +29,6 @@ document = self.scripter.documentcontroller.openDocument(selectedFile) self.scripter.uicontroller.setDocumentEditor(document) self.scripter.uicontroller.setStatusBar(document.filePath) - except: QMessageBox.information(self.scripter.uicontroller.mainWidget, 'Invalid File', diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/runaction/runaction.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/runaction/runaction.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/runaction/runaction.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/runaction/runaction.py @@ -10,19 +10,20 @@ self.scripter = scripter self.editor = self.scripter.uicontroller.editor - self.output = self.scripter.uicontroller.output + self.output = self.scripter.uicontroller.findStackWidget('OutPut') self.triggered.connect(self.run) self.setText('Run') # path to the icon - #self.setIcon(QIcon('/home/eliakincosta/Pictures/play.svg')) + self.setIcon(QIcon('/home/eliakincosta/Pictures/play.svg')) @property def parent(self): return 'toolBar' def run(self): + print('roda roda') stdout = sys.stdout stderr = sys.stderr output = docwrapper.DocWrapper(self.output.document()) diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/saveaction/saveaction.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/saveaction/saveaction.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/saveaction/saveaction.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/saveaction/saveaction.py @@ -1,4 +1,5 @@ -from PyQt5.QtWidgets import QAction, QFileDialog +from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox + class SaveAction(QAction): @@ -19,20 +20,28 @@ def save(self): text = self.editor.toPlainText() fileName = '' + fileExtension = '' if not self.scripter.documentcontroller.activeDocument: - fileName = QFileDialog.getSaveFileName(self.scripter.uicontroller.mainWidget, - 'Save Python File', '', - 'Python File (*.py)')[0] - if not fileName: - return - - fileExtension = fileName.rsplit('.', maxsplit=1)[1] - if not fileExtension=='py': + try: + fileName = QFileDialog.getSaveFileName(self.scripter.uicontroller.mainWidget, + 'Save Python File', '', + 'Python File (*.py)')[0] + if not fileName: + return + + fileExtension = fileName.rsplit('.', maxsplit=1)[1] + except: + if not fileExtension=='py': + QMessageBox.information(self.scripter.uicontroller.mainWidget, + 'Invalid File', + 'Save files with .py extension') return document = self.scripter.documentcontroller.saveDocument(text, fileName) if document: self.scripter.uicontroller.setStatusBar(document.filePath) else: self.scripter.uicontroller.setStatusBar('untitled') + + return document diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/debugarea.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/debugarea.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/debugarea.py @@ -0,0 +1,16 @@ +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * + + +class DebugArea(QWidget): + + def __init__(self, editor): + super(DebugArea, self).__init__(editor) + self.codeEditor = editor + + def sizeHint(self): + return QSize(self.codeEditor.debugAreaWidth(), 0) + + def paintEvent(self, event): + """It Invokes the draw method(debugAreaPaintEvent) in CodeEditor""" + self.codeEditor.debugAreaPaintEvent(event) diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/pythoneditor.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/pythoneditor.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/pythoneditor.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/pythoneditor.py @@ -1,28 +1,34 @@ -# -*- coding: utf-8 -*- - - from PyQt5.QtCore import * from PyQt5.QtWidgets import * -from scripter.ui_scripter.editor import linenumberarea from PyQt5.QtGui import * +from scripter.ui_scripter.editor import linenumberarea, debugarea class CodeEditor(QPlainTextEdit): - def __init__(self, parent=None): + DEBUG_AREA_WIDTH = 20 + + def __init__(self, scripter, parent=None): super(CodeEditor, self).__init__(parent) self.setLineWrapMode(self.NoWrap) + self.scripter = scripter self.lineNumberArea = linenumberarea.LineNumberArea(self) + self.debugArea = debugarea.DebugArea(self) + self.debugIcon = QIcon('/home/eliakincosta/Pictures/debug_arrow.svg') - self.blockCountChanged.connect(self.updateLineNumberAreaWidth) + self.blockCountChanged.connect(self.updateMarginsWidth) self.updateRequest.connect(self.updateLineNumberArea) self.cursorPositionChanged.connect(self.highlightCurrentLine) - self.updateLineNumberAreaWidth() + self.updateMarginsWidth() self.highlightCurrentLine() self.font = "Monospace" + self._stepped = False + + def debugAreaWidth(self): + return self.DEBUG_AREA_WIDTH def lineNumberAreaWidth(self): """The lineNumberAreaWidth is the quatity of decimal places in blockCount""" @@ -37,24 +43,33 @@ return space def resizeEvent(self, event): - super(CodeEditor, self).resizeEvent(event) + super(CodeEditor, self).resizeEvent(event) - qRect = self.contentsRect(); - self.lineNumberArea.setGeometry(QRect(qRect.left(), qRect.top(), self.lineNumberAreaWidth(), qRect.height())); + qRect = self.contentsRect() + self.debugArea.setGeometry(QRect(qRect.left(), + qRect.top(), + self.debugAreaWidth(), + qRect.height())) - def updateLineNumberAreaWidth(self): - self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) + self.lineNumberArea.setGeometry(QRect(qRect.left() + self.debugAreaWidth(), + qRect.top(), + self.lineNumberAreaWidth(), + qRect.height())) + + def updateMarginsWidth(self): + self.setViewportMargins(self.lineNumberAreaWidth() + self.debugAreaWidth(), 0, 0, 0) def updateLineNumberArea(self, rect, dy): """ This slot is invoked when the editors viewport has been scrolled """ if dy: self.lineNumberArea.scroll(0, dy) + self.debugArea.scroll(0, dy) else: self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) if rect.contains(self.viewport().rect()): - self.updateLineNumberAreaWidth() + self.updateMarginsWidth() def lineNumberAreaPaintEvent(self, event): """This method draws the current lineNumberArea for while""" @@ -65,7 +80,6 @@ blockNumber = block.blockNumber() top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top()) bottom = top + int(self.blockBoundingRect(block).height()) - while block.isValid() and top <= event.rect().bottom(): if block.isVisible() and bottom >= event.rect().top(): number = str(blockNumber + 1) @@ -78,6 +92,23 @@ bottom = top + int(self.blockBoundingRect(block).height()) blockNumber += 1 + def debugAreaPaintEvent(self, event): + if self.scripter.debugcontroller.isActive and self.scripter.debugcontroller.currentLine: + lineNumber = self.scripter.debugcontroller.currentLine + block = self.document().findBlockByLineNumber(lineNumber-1) + + if self._stepped: + cursor = QTextCursor(block) + self.setTextCursor(cursor) + self._stepped = False + + top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top()) + bottom = top + int(self.blockBoundingRect(block).height()) + + painter = QPainter(self.debugArea) + pixmap = self.debugIcon.pixmap(QSize(self.debugAreaWidth()-3, int(self.blockBoundingRect(block).height()))) + painter.drawPixmap(QPoint(0, top), pixmap) + def highlightCurrentLine(self): """Highlight current line under cursor""" currentSelection = QTextEdit.ExtraSelection() @@ -110,3 +141,9 @@ def font(self, font): self._font = font self.setFont(QFont(font, 10)) + + def setStepped(self, status): + self._stepped = status + + def repaintDebugArea(self): + self.debugArea.repaint() diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/__init__.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/__init__.py @@ -0,0 +1,2 @@ +widgetClasses = ['outputwidget.outputwidget.OutPutWidget', + 'debuggerwidget.debuggerwidget.DebuggerWidget',] diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/__init__.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/__init__.py new file mode 100644 diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/debuggerwidget.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/debuggerwidget.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/debuggerwidget.py @@ -0,0 +1,32 @@ +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QToolBar, QTableWidget,QAction +from PyQt5.QtGui import QIcon +from . import stepaction, stopaction + +class DebuggerWidget(QWidget): + + def __init__(self, scripter, parent=None): + super(DebuggerWidget, self).__init__(parent) + + self.scripter = scripter + self.setObjectName('Debugger') + self.layout = QVBoxLayout() + + self.toolbar = QToolBar() + self.stopAction = stopaction.StopAction(self.scripter, self) + self.stepAction = stepaction.StepAction(self.scripter, self) + self.toolbar.addAction(self.stopAction) + self.toolbar.addAction(self.stepAction) + self.disableToolbar(True) + + self.table = QTableWidget(4, 4) + + self.layout.addWidget(self.toolbar) + self.layout.addWidget(self.table) + self.setLayout(self.layout) + + def startDebugger(self): + self.disableToolbar(False) + + def disableToolbar(self, status): + for action in self.toolbar.actions(): + action.setDisabled(status) diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/stepaction.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/stepaction.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/stepaction.py @@ -0,0 +1,23 @@ +from PyQt5.QtWidgets import QAction +from PyQt5.QtGui import QIcon + + +class StepAction(QAction): + + def __init__(self, scripter, toolbar, parent=None): + super(StepAction, self).__init__(parent) + self.scripter = scripter + self.toolbar = toolbar + + self.triggered.connect(self.step) + + self.setText('Step Over') + # path to the icon + self.setIcon(QIcon('/home/eliakincosta/Pictures/step.svg')) + + def step(self): + status = self.scripter.debugcontroller.isActive + if status: + self.scripter.debugcontroller.step() + else: + self.toolbar.disableToolbar(True) diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/stopaction.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/stopaction.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/debuggerwidget/stopaction.py @@ -0,0 +1,20 @@ +from PyQt5.QtWidgets import QAction +from PyQt5.QtGui import QIcon + + +class StopAction(QAction): + + def __init__(self, scripter, toolbar, parent=None): + super(StopAction, self).__init__(parent) + self.scripter = scripter + self.toolbar = toolbar + + self.triggered.connect(self.stop) + + self.setText('Stop') + # path to the icon + self.setIcon(QIcon('/home/eliakincosta/Pictures/stop.svg')) + + def stop(self): + self.scripter.debugcontroller.stop() + self.toolbar.disableToolbar(True) diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/outputwidget/__init__.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/outputwidget/__init__.py new file mode 100644 diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/outputwidget/outputwidget.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/outputwidget/outputwidget.py new file mode 100644 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/stackwidgets/outputwidget/outputwidget.py @@ -0,0 +1,10 @@ +from PyQt5.QtWidgets import QPlainTextEdit + + +class OutPutWidget(QPlainTextEdit): + + def __init__(self, scripter, parent=None): + super(OutPutWidget, self).__init__(parent) + + self.scripter = scripter + self.setObjectName('OutPut') diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/uicontroller.py b/plugins/extensions/pykrita/plugin/plugins/scripter/uicontroller.py --- a/plugins/extensions/pykrita/plugin/plugins/scripter/uicontroller.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/uicontroller.py @@ -20,33 +20,38 @@ self.actions = [] self.mainWidget.setWindowModality(Qt.NonModal) - self.editor = pythoneditor.CodeEditor() - self.output = QPlainTextEdit() + def initialize(self, scripter): + self.editor = pythoneditor.CodeEditor(scripter) + self.widgetSelector = QComboBox() + self.stackedWidget = QStackedWidget() self.statusBar = QLabel('untitled') self.highlight = syntax.PythonHighlighter(self.editor.document(), syntaxstyles.DefaultSyntaxStyle()) - def initialize(self, scripter): self.scripter = scripter self.loadMenus() + self.loadWidgets() self.loadActions() + self.widgetSelector.currentIndexChanged.connect(self._currentIndexChanged) + vbox = QVBoxLayout(self.mainWidget) vbox.addWidget(self.menu_bar) vbox.addWidget(self.editor) vbox.addWidget(self.actionToolbar) - vbox.addWidget(self.output) + vbox.addWidget(self.widgetSelector) + vbox.addWidget(self.stackedWidget) vbox.addWidget(self.statusBar) self.mainWidget.resize(400, 500) self.mainWidget.setWindowTitle("Scripter") self.mainWidget.setSizeGripEnabled(True) + self.addMenu('Edit', 'Edit') self.mainWidget.show() self.mainWidget.activateWindow() def loadMenus(self): self.addMenu('File', 'File') - self.addMenu('Edit', 'Edit') def addMenu(self, menuName, parentName): parent = self.menu_bar.findChild(QObject, parentName) @@ -82,21 +87,58 @@ for action in self.actions: action['parent'].addAction(action['action']) + def loadWidgets(self): + modulePath = 'scripter.ui_scripter.stackwidgets' + widgetsModule = importlib.import_module(modulePath) + modules = [] + + for classPath in widgetsModule.widgetClasses: + _module, _klass = classPath.rsplit('.', maxsplit=1) + modules.append(dict(module='{0}.{1}'.format(modulePath, _module), + klass=_klass)) + + for module in modules: + m = importlib.import_module(module['module']) + widgetClass = getattr(m, module['klass']) + obj = widgetClass(self.scripter) + self.stackedWidget.addWidget(obj) + self.widgetSelector.addItem(obj.objectName()) + def invokeAction(self, actionName): for action in self.actions: if action['action'].objectName() == actionName: method = getattr(action['action'], actionName) if method: return method() + def findStackWidget(self, widgetName): + for index in range(self.stackedWidget.count()): + widget = self.stackedWidget.widget(index) + if widget.objectName() == widgetName: + return widget + def setDocumentEditor(self, document): self.editor.clear() - - for line in document.data: - self.editor.appendPlainText(line) + self.editor.appendPlainText(document.data) def setStatusBar(self, value='untitled'): self.statusBar.setText(value) + def setActiveWidget(self, widgetName): + index = self.widgetSelector.findText(widgetName) + + if index!=-1: + self.widgetSelector.setCurrentIndex(index) + + def setStepped(self, status): + self.editor.setStepped(status) + def clearEditor(self): self.editor.clear() + + def _currentIndexChanged(self, index): + if index != -1: + self.stackedWidget.setCurrentIndex(index) + + def repaintDebugArea(self): + self.editor.repaintDebugArea()