109 changes: 97 additions & 12 deletions python/console/console_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
import os
import subprocess
import datetime
import pyclbr
from operator import itemgetter
import traceback

class KeyFilter(QObject):
SHORTCUTS = {
Expand All @@ -56,9 +59,10 @@ def __init__(self, window, tab, *args):
self._handlers[qt_keycode] = handlers

def get_handler(self, key, modifier):
for modifiers, handler in self._handlers.get(key, []):
if modifiers == modifier:
return handler
if self.window.count() > 1:
for modifiers, handler in self._handlers.get(key, []):
if modifiers == modifier:
return handler
return None

def eventFilter(self, obj, event):
Expand All @@ -69,7 +73,6 @@ def eventFilter(self, obj, event):
return QObject.eventFilter(self, obj, event)

class Editor(QsciScintilla):
#ARROW_MARKER_NUM = 4
def __init__(self, parent=None):
super(Editor,self).__init__(parent)
self.parent = parent
Expand Down Expand Up @@ -109,6 +112,7 @@ def __init__(self, parent=None):
# self.ARROW_MARKER_NUM)

self.setMinimumHeight(120)
#self.setMinimumWidth(300)

self.setAutoCompletionThreshold(2)
self.setAutoCompletionSource(self.AcsAPIs)
Expand All @@ -123,9 +127,9 @@ def __init__(self, parent=None):
self.setEdgeColumn(80)
self.setEdgeColor(QColor("#FF0000"))

self.setWrapMode(QsciScintilla.WrapCharacter)
#self.setWrapMode(QsciScintilla.WrapCharacter)
self.setWhitespaceVisibility(QsciScintilla.WsVisibleAfterIndent)
self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)
#self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)

# Annotations
#self.setAnnotationDisplay(QsciScintilla.ANNOTATION_BOXED)
Expand Down Expand Up @@ -272,6 +276,9 @@ def contextMenuEvent(self, e):
"Share on codepad",
self.codepad)
menu.addSeparator()
showCodeInspection = menu.addAction("Hide/Show Object list",
self.objectListEditor)
menu.addSeparator()
selectAllAction = menu.addAction("Select All",
self.selectAll,
QKeySequence.SelectAll)
Expand Down Expand Up @@ -301,6 +308,11 @@ def contextMenuEvent(self, e):
pasteAction.setEnabled(True)
action = menu.exec_(self.mapToGlobal(e.pos()))

def objectListEditor(self):
listObj = self.parent.pc.splitterObj
listObj.hide() if listObj.isVisible() else listObj.show()
self.parent.pc.objectListButton.setChecked(False)

def codepad(self):
import urllib2, urllib
listText = self.selectedText().split('\n')
Expand Down Expand Up @@ -344,6 +356,7 @@ def commentEditorCode(self, commentCheck):
self.beginUndoAction()
if self.hasSelectedText():
startLine, _, endLine, _ = self.getSelection()
self.beginUndoAction()
for line in range(startLine, endLine + 1):
selCmd = self.text(line)
self.setSelection(line, 0, line, selCmd.length())
Expand All @@ -358,6 +371,7 @@ def commentEditorCode(self, commentCheck):
self.insert(selCmd)
else:
line, pos = self.getCursorPosition()
self.beginUndoAction()
selCmd = self.text(line)
self.setSelection(line, 0, line, selCmd.length())
self.removeSelectedText()
Expand Down Expand Up @@ -432,6 +446,17 @@ def getTextFromEditor(self):
text = self.text()
textList = text.split("\n")
return textList

def goToLine(self, objName, linenr):
self.SendScintilla(QsciScintilla.SCI_GOTOLINE, linenr-1)
self.SendScintilla(QsciScintilla.SCI_SETTARGETSTART, self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS))
self.SendScintilla(QsciScintilla.SCI_SETTARGETEND, len(self.text()))
pos = self.SendScintilla(QsciScintilla.SCI_SEARCHINTARGET, len(objName), objName)
index = pos - self.SendScintilla(QsciScintilla.SCI_GETCURRENTPOS)
#line, _ = self.getCursorPosition()
self.setSelection(linenr - 1, index, linenr - 1, index + len(objName))
self.ensureLineVisible(linenr)
self.setFocus()

class EditorTab(QWidget):
def __init__(self, parent, parentConsole, filename, *args):
Expand All @@ -445,12 +470,13 @@ def __init__(self, parent, parentConsole, filename, *args):

self.newEditor = Editor(self)
self.newEditor.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.newEditor.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.newEditor.modificationChanged.connect(self.modified)
if filename:
self.newEditor.setText(open(filename, "r").read())
self.newEditor.setModified(False)
self.path = filename

# Creates layout for message bar
self.layout = QGridLayout(self.newEditor)
self.layout.setContentsMargins(0, 0, 0, 0)
Expand Down Expand Up @@ -499,6 +525,7 @@ def save(self):
self.mw.setTabToolTip(self.mw.currentIndex(), self.path)
self.newEditor.setModified(False)
self.pc.updateTabListScript(self.path, action='append')
self.mw.listObject(self)

def changeFont(self):
self.newEditor.refreshLexerProperties()
Expand Down Expand Up @@ -571,11 +598,6 @@ def __init__(self, parent):
self.settings = QSettings()
tabScripts = self.settings.value("pythonConsole/tabScripts")
self.restoreTabList = tabScripts.toList()

if self.restoreTabList:
self.topFrame.show()
else:
self.newTabEditor(filename=None)

self.setDocumentMode(True)
self.setMovable(True)
Expand All @@ -596,6 +618,8 @@ def __init__(self, parent):
self.fileTabButton.setMenu(self.fileTabMenu)
self.setCornerWidget(self.fileTabButton, Qt.TopRightCorner)
self.connect(self, SIGNAL("tabCloseRequested(int)"), self._removeTab)
self.connect(self, SIGNAL('currentChanged(int)'), self.listObject)
self.connect(self, SIGNAL('currentChanged(int)'), self.changeLastDirPath)

# Open button
self.newTabButton = QToolButton(self)
Expand All @@ -605,6 +629,13 @@ def __init__(self, parent):
self.setCornerWidget(self.newTabButton, Qt.TopLeftCorner)
self.connect(self.newTabButton, SIGNAL('clicked()'), self.newTabEditor)

def checkToRestoreTabs(self):
if self.restoreTabList:
self.topFrame.show()
else:
self.newTabEditor(filename=None)
self.parent.toolBarEditor.setEnabled(True)

def newTabEditor(self, tabName=None, filename=None):
nr = self.count()
if not tabName:
Expand Down Expand Up @@ -711,6 +742,60 @@ def showFileTabMenuTriggered(self, action):
if ok:
self.setCurrentIndex(index)

def listObject(self, tab):
self.parent.listClassMethod.clear()
if isinstance(tab, EditorTab):
tabWidget = self.widget(self.indexOf(tab))
else:
tabWidget = self.widget(tab)
if tabWidget.path:
pathFile, file = os.path.split(str(tabWidget.path))
module, ext = os.path.splitext(file)
#print sys.modules[module]
if pathFile not in sys.path:
sys.path.append(pathFile)

try:
reload(pyclbr)
dictObject = {}
readModule = pyclbr.readmodule(module)
readModuleFunction = pyclbr.readmodule_ex(module)
for name, class_data in sorted(readModule.items(), key=lambda x:x[1].lineno):
if class_data.file == tabWidget.path:
classItem = QTreeWidgetItem()
classItem.setText(0, name)
classItem.setText(1, str(class_data.lineno))
classItem.setToolTip(0, name)
classItem.setIcon(0, QgsApplication.getThemeIcon("console/iconClassTreeWidgetConsole.png"))
dictObject[name] = class_data.lineno
for meth, lineno in sorted(class_data.methods.items(), key=itemgetter(1)):
methodItem = QTreeWidgetItem()
methodItem.setText(0, meth)
methodItem.setText(1, str(lineno))
methodItem.setToolTip(0, meth)
methodItem.setIcon(0, QgsApplication.getThemeIcon("console/iconMethodTreeWidgetConsole.png"))
classItem.addChild(methodItem)
dictObject[meth] = lineno
self.parent.listClassMethod.addTopLevelItem(classItem)
for func_name, data in sorted(readModuleFunction.items(), key=lambda x:x[1].lineno):
if isinstance(data, pyclbr.Function) and data.file == tabWidget.path:
funcItem = QTreeWidgetItem()
funcItem.setText(0, func_name)
funcItem.setText(1, str(data.lineno))
funcItem.setToolTip(0, func_name)
funcItem.setIcon(0, QgsApplication.getThemeIcon("console/iconFunctionTreeWidgetConsole.png"))
dictObject[func_name] = data.lineno
self.parent.listClassMethod.addTopLevelItem(funcItem)
except:
s = traceback.format_exc()
print '## Error: '
sys.stderr.write(s)

def changeLastDirPath(self, tab):
tabWidget = self.widget(tab)
settings = QSettings()
settings.setValue("pythonConsole/lastDirPath", QVariant(tabWidget.path))

def widgetMessageBar(self, iface, text):
timeout = iface.messageTimeout()
currWidget = self.currentWidget()
Expand Down