mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-23 00:02:38 -05:00
618 lines
24 KiB
Python
618 lines
24 KiB
Python
# -*- coding:utf-8 -*-
|
|
"""
|
|
/***************************************************************************
|
|
Python Console for QGIS
|
|
-------------------
|
|
begin : 2012-09-10
|
|
copyright : (C) 2012 by Salvatore Larosa
|
|
email : lrssvtml (at) gmail (dot) com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
Some portions of code were taken from https://code.google.com/p/pydee/
|
|
"""
|
|
|
|
from qgis.PyQt.QtCore import Qt, QByteArray, QCoreApplication, QFile, QSize
|
|
from qgis.PyQt.QtWidgets import QDialog, QMenu, QShortcut, QApplication
|
|
from qgis.PyQt.QtGui import QKeySequence, QFontMetrics, QStandardItemModel, QStandardItem, QClipboard
|
|
from qgis.PyQt.Qsci import QsciScintilla
|
|
from qgis.gui import QgsCodeEditorPython, QgsCodeEditorColorScheme
|
|
|
|
import sys
|
|
import os
|
|
import code
|
|
import codecs
|
|
import re
|
|
import traceback
|
|
|
|
from qgis.core import QgsApplication, QgsSettings, Qgis
|
|
from qgis.gui import QgsCodeEditor
|
|
|
|
from .ui_console_history_dlg import Ui_HistoryDialogPythonConsole
|
|
|
|
_init_commands = ["import sys", "import os", "import re", "import math", "from qgis.core import *",
|
|
"from qgis.gui import *", "from qgis.analysis import *", "from qgis._3d import *",
|
|
"import processing", "import qgis.utils",
|
|
"from qgis.utils import iface", "from qgis.PyQt.QtCore import *", "from qgis.PyQt.QtGui import *",
|
|
"from qgis.PyQt.QtWidgets import *",
|
|
"from qgis.PyQt.QtNetwork import *", "from qgis.PyQt.QtXml import *"]
|
|
_historyFile = os.path.join(QgsApplication.qgisSettingsDirPath(), "console_history.txt")
|
|
|
|
|
|
class ShellScintilla(QgsCodeEditorPython, code.InteractiveInterpreter):
|
|
|
|
def __init__(self, parent=None):
|
|
super(QgsCodeEditorPython, self).__init__(parent)
|
|
code.InteractiveInterpreter.__init__(self, locals=None)
|
|
|
|
self.parent = parent
|
|
|
|
self.opening = ['(', '{', '[', "'", '"']
|
|
self.closing = [')', '}', ']', "'", '"']
|
|
|
|
self.settings = QgsSettings()
|
|
|
|
self.new_input_line = True
|
|
|
|
self.buffer = []
|
|
self.continuationLine = False
|
|
|
|
self.displayPrompt(self.continuationLine)
|
|
|
|
for line in _init_commands:
|
|
self.runsource(line)
|
|
|
|
self.history = []
|
|
self.softHistory = ['']
|
|
self.softHistoryIndex = 0
|
|
# Read history command file
|
|
self.readHistoryFile()
|
|
|
|
self.historyDlg = HistoryDialog(self)
|
|
|
|
self.refreshSettingsShell()
|
|
|
|
# Don't want to see the horizontal scrollbar at all
|
|
# Use raw message to Scintilla here (all messages are documented
|
|
# here: http://www.scintilla.org/ScintillaDoc.html)
|
|
self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)
|
|
|
|
# not too small
|
|
# self.setMinimumSize(500, 300)
|
|
|
|
self.setWrapMode(QsciScintilla.WrapCharacter)
|
|
self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER)
|
|
|
|
# Disable command key
|
|
ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16
|
|
self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl)
|
|
self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl)
|
|
self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl)
|
|
self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z') + ctrl)
|
|
self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y') + ctrl)
|
|
self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift)
|
|
|
|
# New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete
|
|
self.newShortcutCSS = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Space), self)
|
|
self.newShortcutCAS = QShortcut(QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self)
|
|
self.newShortcutCSS.setContext(Qt.WidgetShortcut)
|
|
self.newShortcutCAS.setContext(Qt.WidgetShortcut)
|
|
self.newShortcutCAS.activated.connect(self.autoComplete)
|
|
self.newShortcutCSS.activated.connect(self.showHistory)
|
|
|
|
def initializeLexer(self):
|
|
super().initializeLexer()
|
|
self.setCaretLineVisible(False)
|
|
self.setLineNumbersVisible(False) # NO linenumbers for the input line
|
|
self.setFoldingVisible(False)
|
|
# Margin 1 is used for the '>>>' prompt (console input)
|
|
self.setMarginLineNumbers(1, True)
|
|
self.setMarginWidth(1, "00000")
|
|
self.setMarginType(1, 5) # TextMarginRightJustified=5
|
|
self.setMarginsBackgroundColor(self.color(QgsCodeEditorColorScheme.ColorRole.Background))
|
|
self.setEdgeMode(QsciScintilla.EdgeNone)
|
|
|
|
def _setMinimumHeight(self):
|
|
font = self.lexer().defaultFont(0)
|
|
fm = QFontMetrics(font)
|
|
|
|
self.setMinimumHeight(fm.height() + 10)
|
|
|
|
def refreshSettingsShell(self):
|
|
# Set Python lexer
|
|
self.initializeLexer()
|
|
|
|
# Sets minimum height for input area based of font metric
|
|
self._setMinimumHeight()
|
|
|
|
def showHistory(self):
|
|
if not self.historyDlg.isVisible():
|
|
self.historyDlg.show()
|
|
self.historyDlg._reloadHistory()
|
|
self.historyDlg.activateWindow()
|
|
|
|
def commandConsole(self, commands):
|
|
if not self.is_cursor_on_last_line():
|
|
self.move_cursor_to_end()
|
|
for cmd in commands:
|
|
self.setText(cmd)
|
|
self.entered()
|
|
self.move_cursor_to_end()
|
|
self.setFocus()
|
|
|
|
def getText(self):
|
|
""" Get the text as a unicode string. """
|
|
value = self.getBytes().decode('utf-8')
|
|
# print (value) printing can give an error because the console font
|
|
# may not have all unicode characters
|
|
return value
|
|
|
|
def getBytes(self):
|
|
""" Get the text as bytes (utf-8 encoded). This is how
|
|
the data is stored internally. """
|
|
len = self.SendScintilla(self.SCI_GETLENGTH) + 1
|
|
bb = QByteArray(len, '0')
|
|
self.SendScintilla(self.SCI_GETTEXT, len, bb)
|
|
return bytes(bb)[:-1]
|
|
|
|
def getTextLength(self):
|
|
return self.SendScintilla(QsciScintilla.SCI_GETLENGTH)
|
|
|
|
def get_end_pos(self):
|
|
"""Return (line, index) position of the last character"""
|
|
line = self.lines() - 1
|
|
return line, len(self.text(line))
|
|
|
|
def is_cursor_at_start(self):
|
|
"""Return True if cursor is at the end of text"""
|
|
cline, cindex = self.getCursorPosition()
|
|
return cline == 0 and cindex == 0
|
|
|
|
def is_cursor_at_end(self):
|
|
"""Return True if cursor is at the end of text"""
|
|
cline, cindex = self.getCursorPosition()
|
|
return (cline, cindex) == self.get_end_pos()
|
|
|
|
def move_cursor_to_start(self):
|
|
"""Move cursor to start of text"""
|
|
self.setCursorPosition(0, 0)
|
|
self.ensureCursorVisible()
|
|
self.ensureLineVisible(0)
|
|
self.displayPrompt(self.continuationLine)
|
|
|
|
def move_cursor_to_end(self):
|
|
"""Move cursor to end of text"""
|
|
line, index = self.get_end_pos()
|
|
self.setCursorPosition(line, index)
|
|
self.ensureCursorVisible()
|
|
self.ensureLineVisible(line)
|
|
self.displayPrompt(self.continuationLine)
|
|
|
|
def is_cursor_on_last_line(self):
|
|
"""Return True if cursor is on the last line"""
|
|
cline, _ = self.getCursorPosition()
|
|
return cline == self.lines() - 1
|
|
|
|
def new_prompt(self, prompt):
|
|
"""
|
|
Print a new prompt and save its (line, index) position
|
|
"""
|
|
self.write(prompt, prompt=True)
|
|
# now we update our cursor giving end of prompt
|
|
line, index = self.getCursorPosition()
|
|
self.ensureCursorVisible()
|
|
self.ensureLineVisible(line)
|
|
|
|
def displayPrompt(self, more=False):
|
|
self.SendScintilla(QsciScintilla.SCI_MARGINSETTEXT, 0, str.encode("..." if more else ">>>"))
|
|
|
|
def syncSoftHistory(self):
|
|
self.softHistory = self.history[:]
|
|
self.softHistory.append('')
|
|
self.softHistoryIndex = len(self.softHistory) - 1
|
|
|
|
def updateSoftHistory(self):
|
|
self.softHistory[self.softHistoryIndex] = self.text()
|
|
|
|
def updateHistory(self, command, skipSoftHistory=False):
|
|
if isinstance(command, list):
|
|
for line in command:
|
|
self.history.append(line)
|
|
elif not command == "":
|
|
if len(self.history) <= 0 or \
|
|
command != self.history[-1]:
|
|
self.history.append(command)
|
|
if not skipSoftHistory:
|
|
self.syncSoftHistory()
|
|
|
|
def writeHistoryFile(self, fromCloseConsole=False):
|
|
ok = False
|
|
try:
|
|
wH = codecs.open(_historyFile, 'w', encoding='utf-8')
|
|
for s in self.history:
|
|
wH.write(s + '\n')
|
|
ok = True
|
|
except:
|
|
raise
|
|
wH.close()
|
|
if ok and not fromCloseConsole:
|
|
msgText = QCoreApplication.translate('PythonConsole',
|
|
'History saved successfully.')
|
|
self.parent.callWidgetMessageBar(msgText)
|
|
|
|
def readHistoryFile(self):
|
|
fileExist = QFile.exists(_historyFile)
|
|
if fileExist:
|
|
with codecs.open(_historyFile, 'r', encoding='utf-8') as rH:
|
|
for line in rH:
|
|
if line != "\n":
|
|
l = line.rstrip('\n')
|
|
self.updateHistory(l, True)
|
|
self.syncSoftHistory()
|
|
else:
|
|
return
|
|
|
|
def clearHistory(self, clearSession=False):
|
|
if clearSession:
|
|
self.history = []
|
|
self.syncSoftHistory()
|
|
msgText = QCoreApplication.translate('PythonConsole',
|
|
'Session and file history cleared successfully.')
|
|
self.parent.callWidgetMessageBar(msgText)
|
|
return
|
|
ok = False
|
|
try:
|
|
cH = codecs.open(_historyFile, 'w', encoding='utf-8')
|
|
ok = True
|
|
except:
|
|
raise
|
|
cH.close()
|
|
if ok:
|
|
msgText = QCoreApplication.translate('PythonConsole',
|
|
'History cleared successfully.')
|
|
self.parent.callWidgetMessageBar(msgText)
|
|
|
|
def clearHistorySession(self):
|
|
self.clearHistory(True)
|
|
|
|
def showPrevious(self):
|
|
if self.softHistoryIndex < len(self.softHistory) - 1 and self.softHistory:
|
|
self.softHistoryIndex += 1
|
|
self.setText(self.softHistory[self.softHistoryIndex])
|
|
self.move_cursor_to_end()
|
|
# self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
|
|
|
|
def showNext(self):
|
|
if self.softHistoryIndex > 0 and self.softHistory:
|
|
self.softHistoryIndex -= 1
|
|
self.setText(self.softHistory[self.softHistoryIndex])
|
|
self.move_cursor_to_end()
|
|
# self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
|
|
|
|
def keyPressEvent(self, e):
|
|
# update the live history
|
|
self.updateSoftHistory()
|
|
|
|
startLine, startPos, endLine, endPos = self.getSelection()
|
|
|
|
# handle invalid cursor position and multiline selections
|
|
if startLine < endLine:
|
|
# allow copying and selecting
|
|
if e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier):
|
|
if e.key() == Qt.Key_C:
|
|
# only catch and return from Ctrl-C here if there's a selection
|
|
if self.hasSelectedText():
|
|
QsciScintilla.keyPressEvent(self, e)
|
|
return
|
|
elif e.key() == Qt.Key_A:
|
|
QsciScintilla.keyPressEvent(self, e)
|
|
return
|
|
else:
|
|
return
|
|
# allow selection
|
|
if e.modifiers() & Qt.ShiftModifier:
|
|
if e.key() in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Home, Qt.Key_End):
|
|
QsciScintilla.keyPressEvent(self, e)
|
|
return
|
|
# all other keystrokes get sent to the input line
|
|
self.move_cursor_to_end()
|
|
|
|
if e.modifiers() & (
|
|
Qt.ControlModifier | Qt.MetaModifier) and e.key() == Qt.Key_C and not self.hasSelectedText():
|
|
# keyboard interrupt
|
|
sys.stdout.fire_keyboard_interrupt = True
|
|
return
|
|
|
|
line, index = self.getCursorPosition()
|
|
cmd = self.text(line)
|
|
hasSelectedText = self.hasSelectedText()
|
|
|
|
if e.key() in (Qt.Key_Return, Qt.Key_Enter) and not self.isListActive():
|
|
self.entered()
|
|
|
|
elif e.key() in (Qt.Key_Left, Qt.Key_Home):
|
|
QsciScintilla.keyPressEvent(self, e)
|
|
|
|
elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete):
|
|
QsciScintilla.keyPressEvent(self, e)
|
|
self.recolor()
|
|
|
|
elif (e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier) and e.key() == Qt.Key_V) or \
|
|
(e.modifiers() & Qt.ShiftModifier and e.key() == Qt.Key_Insert):
|
|
self.paste()
|
|
e.accept()
|
|
|
|
elif e.key() == Qt.Key_Down and not self.isListActive():
|
|
self.showPrevious()
|
|
|
|
elif e.key() == Qt.Key_Up and not self.isListActive():
|
|
self.showNext()
|
|
|
|
# TODO: press event for auto-completion file directory
|
|
else:
|
|
t = e.text()
|
|
self.autoCloseBracket = self.settings.value("pythonConsole/autoCloseBracket", False, type=bool)
|
|
self.autoImport = self.settings.value("pythonConsole/autoInsertionImport", True, type=bool)
|
|
# Close bracket automatically
|
|
if t in self.opening and self.autoCloseBracket:
|
|
i = self.opening.index(t)
|
|
if self.hasSelectedText() and startPos != 0:
|
|
selText = self.selectedText()
|
|
self.removeSelectedText()
|
|
self.insert(self.opening[i] + selText + self.closing[i])
|
|
self.setCursorPosition(endLine, endPos + 2)
|
|
return
|
|
elif t == '(' and (re.match(r'^[ \t]*def \w+$', cmd)
|
|
or re.match(r'^[ \t]*class \w+$', cmd)):
|
|
self.insert('):')
|
|
else:
|
|
self.insert(self.closing[i])
|
|
# FIXES #8392 (automatically removes the redundant char
|
|
# when autoclosing brackets option is enabled)
|
|
elif t in [')', ']', '}'] and self.autoCloseBracket:
|
|
try:
|
|
if cmd[index - 1] in self.opening and t == cmd[index]:
|
|
self.setCursorPosition(line, index + 1)
|
|
self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
|
|
except IndexError:
|
|
pass
|
|
elif t == ' ' and self.autoImport:
|
|
ptrn = r'^[ \t]*from [\w.]+$'
|
|
if re.match(ptrn, cmd):
|
|
self.insert(' import')
|
|
self.setCursorPosition(line, index + 7)
|
|
QsciScintilla.keyPressEvent(self, e)
|
|
|
|
self.displayPrompt(self.continuationLine)
|
|
|
|
def contextMenuEvent(self, e):
|
|
menu = QMenu(self)
|
|
subMenu = QMenu(menu)
|
|
titleHistoryMenu = QCoreApplication.translate("PythonConsole", "Command History")
|
|
subMenu.setTitle(titleHistoryMenu)
|
|
subMenu.addAction(
|
|
QCoreApplication.translate("PythonConsole", "Show"),
|
|
self.showHistory, 'Ctrl+Shift+SPACE')
|
|
subMenu.addAction(
|
|
QCoreApplication.translate("PythonConsole", "Clear File"),
|
|
self.clearHistory)
|
|
subMenu.addAction(
|
|
QCoreApplication.translate("PythonConsole", "Clear Session"),
|
|
self.clearHistorySession)
|
|
menu.addMenu(subMenu)
|
|
menu.addSeparator()
|
|
copyAction = menu.addAction(
|
|
QgsApplication.getThemeIcon("mActionEditCopy.svg"),
|
|
QCoreApplication.translate("PythonConsole", "Copy"),
|
|
self.copy, QKeySequence.Copy)
|
|
pasteAction = menu.addAction(
|
|
QgsApplication.getThemeIcon("mActionEditPaste.svg"),
|
|
QCoreApplication.translate("PythonConsole", "Paste"),
|
|
self.paste, QKeySequence.Paste)
|
|
pyQGISHelpAction = menu.addAction(QgsApplication.getThemeIcon("console/iconHelpConsole.svg"),
|
|
QCoreApplication.translate("PythonConsole", "Search Selected in PyQGIS docs"),
|
|
self.searchSelectedTextInPyQGISDocs)
|
|
copyAction.setEnabled(False)
|
|
pasteAction.setEnabled(False)
|
|
pyQGISHelpAction.setEnabled(False)
|
|
if self.hasSelectedText():
|
|
copyAction.setEnabled(True)
|
|
pyQGISHelpAction.setEnabled(True)
|
|
if QApplication.clipboard().text():
|
|
pasteAction.setEnabled(True)
|
|
menu.exec_(self.mapToGlobal(e.pos()))
|
|
|
|
def mousePressEvent(self, e):
|
|
"""
|
|
Re-implemented to handle the mouse press event.
|
|
e: the mouse press event (QMouseEvent)
|
|
"""
|
|
self.setFocus()
|
|
if e.button() == Qt.MidButton:
|
|
stringSel = QApplication.clipboard().text(QClipboard.Selection)
|
|
if not self.is_cursor_on_last_line():
|
|
self.move_cursor_to_end()
|
|
self.insertFromDropPaste(stringSel)
|
|
e.accept()
|
|
else:
|
|
QsciScintilla.mousePressEvent(self, e)
|
|
|
|
def paste(self):
|
|
"""
|
|
Method to display data from the clipboard.
|
|
|
|
XXX: It should reimplement the virtual QScintilla.paste method,
|
|
but it seems not used by QScintilla code.
|
|
"""
|
|
stringPaste = QApplication.clipboard().text()
|
|
if self.is_cursor_on_last_line():
|
|
if self.hasSelectedText():
|
|
self.removeSelectedText()
|
|
else:
|
|
self.move_cursor_to_end()
|
|
self.insertFromDropPaste(stringPaste)
|
|
|
|
# Drag and drop
|
|
def dropEvent(self, e):
|
|
if e.mimeData().hasText():
|
|
stringDrag = e.mimeData().text()
|
|
self.insertFromDropPaste(stringDrag)
|
|
self.setFocus()
|
|
e.setDropAction(Qt.CopyAction)
|
|
e.accept()
|
|
else:
|
|
QsciScintilla.dropEvent(self, e)
|
|
|
|
def insertFromDropPaste(self, textDP):
|
|
pasteList = textDP.splitlines()
|
|
if pasteList:
|
|
for line in pasteList[:-1]:
|
|
cleanLine = line.replace(">>> ", "").replace("... ", "")
|
|
self.insert(cleanLine)
|
|
self.move_cursor_to_end()
|
|
self.runCommand(self.text())
|
|
if pasteList[-1] != "":
|
|
line = pasteList[-1]
|
|
cleanLine = line.replace(">>> ", "").replace("... ", "")
|
|
curpos = self.getCursorPosition()
|
|
self.insert(cleanLine)
|
|
self.setCursorPosition(curpos[0], curpos[1] + len(cleanLine))
|
|
|
|
def insertTextFromFile(self, listOpenFile):
|
|
for line in listOpenFile[:-1]:
|
|
self.append(line)
|
|
self.move_cursor_to_end()
|
|
self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
|
|
self.runCommand(self.text())
|
|
self.append(listOpenFile[-1])
|
|
self.move_cursor_to_end()
|
|
self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
|
|
|
|
def entered(self):
|
|
self.move_cursor_to_end()
|
|
self.runCommand(self.text())
|
|
self.setFocus()
|
|
self.move_cursor_to_end()
|
|
|
|
def runCommand(self, cmd):
|
|
self.writeCMD(cmd)
|
|
import webbrowser
|
|
self.updateHistory(cmd)
|
|
version = 'master' if 'master' in Qgis.QGIS_VERSION.lower() else re.findall(r'^\d.[0-9]*', Qgis.QGIS_VERSION)[0]
|
|
if cmd in ('_pyqgis', '_api', '_cookbook'):
|
|
if cmd == '_pyqgis':
|
|
webbrowser.open("https://qgis.org/pyqgis/{}".format(version))
|
|
elif cmd == '_api':
|
|
webbrowser.open("https://qgis.org/api/{}".format('' if version == 'master' else version))
|
|
elif cmd == '_cookbook':
|
|
webbrowser.open("https://docs.qgis.org/{}/en/docs/pyqgis_developer_cookbook/".format(
|
|
'testing' if version == 'master' else version))
|
|
else:
|
|
self.buffer.append(cmd)
|
|
src = "\n".join(self.buffer)
|
|
more = self.runsource(src)
|
|
self.continuationLine = True
|
|
if not more:
|
|
self.continuationLine = False
|
|
self.buffer = []
|
|
|
|
# prevents to commands with more lines to break the console
|
|
# in the case they have a eol different from '\n'
|
|
self.setText('')
|
|
self.move_cursor_to_end()
|
|
self.displayPrompt(self.continuationLine)
|
|
|
|
def write(self, txt):
|
|
sys.stderr.write(txt)
|
|
|
|
def writeCMD(self, txt):
|
|
if sys.stdout:
|
|
sys.stdout.fire_keyboard_interrupt = False
|
|
if len(txt) > 0:
|
|
prompt = "... " if self.continuationLine else ">>> "
|
|
sys.stdout.write(prompt + txt + '\n')
|
|
|
|
def runsource(self, source, filename='<input>', symbol='single'):
|
|
if sys.stdout:
|
|
sys.stdout.fire_keyboard_interrupt = False
|
|
hook = sys.excepthook
|
|
try:
|
|
def excepthook(etype, value, tb):
|
|
self.write("".join(traceback.format_exception(etype, value, tb)))
|
|
|
|
sys.excepthook = excepthook
|
|
|
|
return super(ShellScintilla, self).runsource(source, filename, symbol)
|
|
finally:
|
|
sys.excepthook = hook
|
|
|
|
|
|
class HistoryDialog(QDialog, Ui_HistoryDialogPythonConsole):
|
|
|
|
def __init__(self, parent):
|
|
QDialog.__init__(self, parent)
|
|
self.setupUi(self)
|
|
self.parent = parent
|
|
self.setWindowTitle(QCoreApplication.translate("PythonConsole",
|
|
"Python Console - Command History"))
|
|
self.listView.setToolTip(QCoreApplication.translate("PythonConsole",
|
|
"Double-click on item to execute"))
|
|
self.model = QStandardItemModel(self.listView)
|
|
|
|
self._reloadHistory()
|
|
|
|
self.deleteScut = QShortcut(QKeySequence(Qt.Key_Delete), self)
|
|
self.deleteScut.activated.connect(self._deleteItem)
|
|
self.listView.doubleClicked.connect(self._runHistory)
|
|
self.reloadHistory.clicked.connect(self._reloadHistory)
|
|
self.saveHistory.clicked.connect(self._saveHistory)
|
|
self.runHistoryButton.clicked.connect(self._executeSelectedHistory)
|
|
|
|
def _executeSelectedHistory(self):
|
|
items = self.listView.selectionModel().selectedIndexes()
|
|
items.sort()
|
|
for item in items:
|
|
self.parent.runCommand(item.data(Qt.DisplayRole))
|
|
|
|
def _runHistory(self, item):
|
|
cmd = item.data(Qt.DisplayRole)
|
|
self.parent.runCommand(cmd)
|
|
|
|
def _saveHistory(self):
|
|
self.parent.writeHistoryFile(True)
|
|
|
|
def _reloadHistory(self):
|
|
self.model.clear()
|
|
item = None
|
|
for i in self.parent.history:
|
|
item = QStandardItem(i)
|
|
if sys.platform.startswith('win'):
|
|
item.setSizeHint(QSize(18, 18))
|
|
self.model.appendRow(item)
|
|
|
|
self.listView.setModel(self.model)
|
|
self.listView.scrollToBottom()
|
|
if item:
|
|
self.listView.setCurrentIndex(self.model.indexFromItem(item))
|
|
|
|
def _deleteItem(self):
|
|
itemsSelected = self.listView.selectionModel().selectedIndexes()
|
|
if itemsSelected:
|
|
item = itemsSelected[0].row()
|
|
# Remove item from the command history (just for the current session)
|
|
self.parent.history.pop(item)
|
|
self.parent.softHistory.pop(item)
|
|
if item < self.parent.softHistoryIndex:
|
|
self.parent.softHistoryIndex -= 1
|
|
self.parent.setText(self.parent.softHistory[self.parent.softHistoryIndex])
|
|
self.parent.move_cursor_to_end()
|
|
# Remove row from the command history dialog
|
|
self.model.removeRow(item)
|