Основанный на QScintilla текстовый редактор в PyQt5 с интерактивными функциями и переменными
Я пытаюсь сделать простой текстовый редактор с подсветкой основного синтаксиса, дополнением кода и интерактивными функциями и переменными в PyQt5. Моя лучшая надежда для достижения этого - использование порта QScintilla.
для PyQt5.
Я нашел следующий пример текстового редактора на основе QScintilla на сайте Эли Бендерского ( http://eli.thegreenplace.net/2011/04/01/sample-using-qscintilla-with-pyqt, Виктор С. адаптировал его для PyQt5). Я думаю, что этот пример является хорошей отправной точкой:
#-------------------------------------------------------------------------
# qsci_simple_pythoneditor.pyw
#
# QScintilla sample with PyQt
#
# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------
import sys
import sip
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.Qsci import QsciScintilla, QsciLexerPython
class SimplePythonEditor(QsciScintilla):
ARROW_MARKER_NUM = 8
def __init__(self, parent=None):
super(SimplePythonEditor, self).__init__(parent)
# Set the default font
font = QFont()
font.setFamily('Courier')
font.setFixedPitch(True)
font.setPointSize(10)
self.setFont(font)
self.setMarginsFont(font)
# Margin 0 is used for line numbers
fontmetrics = QFontMetrics(font)
self.setMarginsFont(font)
self.setMarginWidth(0, fontmetrics.width("00000") + 6)
self.setMarginLineNumbers(0, True)
self.setMarginsBackgroundColor(QColor("#cccccc"))
# Clickable margin 1 for showing markers
self.setMarginSensitivity(1, True)
# self.connect(self,
# SIGNAL('marginClicked(int, int, Qt::KeyboardModifiers)'),
# self.on_margin_clicked)
self.markerDefine(QsciScintilla.RightArrow,
self.ARROW_MARKER_NUM)
self.setMarkerBackgroundColor(QColor("#ee1111"),
self.ARROW_MARKER_NUM)
# Brace matching: enable for a brace immediately before or after
# the current position
#
self.setBraceMatching(QsciScintilla.SloppyBraceMatch)
# Current line visible with special background color
self.setCaretLineVisible(True)
self.setCaretLineBackgroundColor(QColor("#ffe4e4"))
# Set Python lexer
# Set style for Python comments (style number 1) to a fixed-width
# courier.
#
lexer = QsciLexerPython()
lexer.setDefaultFont(font)
self.setLexer(lexer)
text = bytearray(str.encode("Arial"))
# 32, "Courier New"
self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, text)
# 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(600, 450)
def on_margin_clicked(self, nmargin, nline, modifiers):
# Toggle marker for the line the margin was clicked on
if self.markersAtLine(nline) != 0:
self.markerDelete(nline, self.ARROW_MARKER_NUM)
else:
self.markerAdd(nline, self.ARROW_MARKER_NUM)
if __name__ == "__main__":
app = QApplication(sys.argv)
editor = SimplePythonEditor()
editor.show()
editor.setText(open(sys.argv[0]).read())
app.exec_()
Просто скопируйте и вставьте этот код в пустой .py
файл, и запустите его. На вашем дисплее должен появиться следующий простой текстовый редактор:
Обратите внимание, насколько прекрасна подсветка синтаксиса! QScintilla, конечно, сделал некоторый анализ на заднем плане, чтобы добиться этого.
Можно ли сделать кликабельные функции и переменные для этого текстового редактора? У каждого уважающего себя IDE есть это. Вы нажимаете на функцию, и среда IDE переходит к определению функции. То же самое для переменных. Я бы хотел знать:
- Поддерживает ли QScintilla интерактивные функции и переменные?
- Если нет, возможно ли импортировать другой модуль Python, который реализует эту функцию в текстовом редакторе QScintilla?
РЕДАКТИРОВАТЬ:
Юзер отметил следующее:
Для кликабельных имен функций требуется полный анализ с глубоким знанием языка программирования [..]
Это далеко выходит за рамки Scintilla/QScintilla. Scintilla предоставляет способ реагирования, когда мышь щелкает где-то по тексту, но логика "где определение функции" отсутствует в Scintilla и, вероятно, никогда не будет.
Однако некоторые проекты посвящены этой задаче, например ctags. Вы можете просто написать обертку вокруг этого вида инструмента.
Я думаю, что написание такой оболочки для ctags теперь в моем списке TODO. Самый первый шаг - получить реакцию (сигнал Qt), когда пользователь нажимает на функцию или переменную. И, возможно, функция / переменная должна стать немного голубоватой, когда вы наводите курсор мыши над ней, чтобы уведомить пользователя о том, что она активна. Я уже пытался добиться этого, но сдерживается нехваткой документации QScintilla.
Итак, позвольте нам урезать вопрос так: как сделать функцию или переменную в текстовом редакторе QScintilla кликабельной (кликабельная определяется как "что-то происходит")
РЕДАКТИРОВАТЬ:
Я только что вернулся к этому вопросу сейчас - несколько месяцев спустя. Я сотрудничаю с моим другом Матичем Куковцем в разработке веб-сайта о QScintilla. Это удобный для начинающих урок о том, как его использовать:
Я надеюсь, что эта инициатива восполняет пробел в отсутствии документации.
3 ответа
Способ использовать Pyqt5 с опцией с интерактивными функциями и переменными. Ваш скрипт, имеющий кликабельную часть в кавычках, будет выглядеть так в PyQt5 с пользовательским сигналом.
СИГНАЛ PyQt4
self.connect(self,SIGNAL('marginClicked(int, int, Qt::KeyboardModifiers)'),
self.on_margin_clicked)
СИГНАЛ PyQt5
self.marginClicked.connect(self.on_margin_clicked)
PyQt5
import sys
import sip
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.Qsci import QsciScintilla, QsciLexerPython
class SimplePythonEditor(QsciScintilla):
ARROW_MARKER_NUM = 8
def __init__(self, parent=None):
super(SimplePythonEditor, self).__init__(parent)
# Set the default font
font = QFont()
font.setFamily('Courier')
font.setFixedPitch(True)
font.setPointSize(10)
self.setFont(font)
self.setMarginsFont(font)
# Margin 0 is used for line numbers
fontmetrics = QFontMetrics(font)
self.setMarginsFont(font)
self.setMarginWidth(0, fontmetrics.width("00000") + 6)
self.setMarginLineNumbers(0, True)
self.setMarginsBackgroundColor(QColor("#cccccc"))
# Clickable margin 1 for showing markers
self.setMarginSensitivity(1, True)
self.marginClicked.connect(self.on_margin_clicked)
self.markerDefine(QsciScintilla.RightArrow,
self.ARROW_MARKER_NUM)
self.setMarkerBackgroundColor(QColor("#ee1111"),
self.ARROW_MARKER_NUM)
# Brace matching: enable for a brace immediately before or after
# the current position
#
self.setBraceMatching(QsciScintilla.SloppyBraceMatch)
# Current line visible with special background color
self.setCaretLineVisible(True)
self.setCaretLineBackgroundColor(QColor("#ffe4e4"))
# Set Python lexer
# Set style for Python comments (style number 1) to a fixed-width
# courier.
#
lexer = QsciLexerPython()
lexer.setDefaultFont(font)
self.setLexer(lexer)
text = bytearray(str.encode("Arial"))
# 32, "Courier New"
self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, text)
# 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(600, 450)
def on_margin_clicked(self, nmargin, nline, modifiers):
# Toggle marker for the line the margin was clicked on
if self.markersAtLine(nline) != 0:
self.markerDelete(nline, self.ARROW_MARKER_NUM)
else:
self.markerAdd(nline, self.ARROW_MARKER_NUM)
if __name__ == "__main__":
app = QApplication(sys.argv)
editor = SimplePythonEditor()
editor.show()
editor.setText(open(sys.argv[0]).read())
app.exec_()
Подсветка синтаксиса - это просто запуск лексера исходного файла для поиска токенов, а затем присвоение ему стилей. Лексер имеет очень базовое понимание языка программирования, он понимает только то, что представляет собой числовой литерал, ключевое слово, оператор, комментарий, несколько других и все. Это довольно простая работа, которую можно выполнить только с помощью регулярных выражений.
С другой стороны, для кликабельных имен функций требуется полный анализ с более глубоким знанием языка программирования, например, является ли это объявлением переменной или использования и т. Д. Кроме того, для этого может потребоваться анализ других исходных файлов, не открытых текущим редактором.,
Это далеко выходит за рамки Scintilla/QScintilla. Scintilla предоставляет способ реагирования, когда мышь щелкает где-то по тексту, но логика "где определение функции" отсутствует в Scintilla и, вероятно, никогда не будет.
Однако некоторые проекты посвящены этой задаче, например ctags. Вы можете просто написать обертку вокруг этого вида инструмента.
Я получил полезный ответ от Матика Куковца по почте, которым я хотел бы поделиться здесь. Matic Kukovec сделал потрясающую IDE на основе QScintilla: https://github.com/matkuki/ExCo. Возможно, это вдохновит больше людей углубляться в QScintilla (и кликабельные переменные и функции).
Горячие точки делают текст кликабельным. Вы должны стилизовать его вручную, используя QScintilla.SendScintilla
функция. Пример функции, которую я использовал в моем редакторе Ex.Co. ( https://github.com/matkuki/ExCo):
def style_hotspot(self, index_from, length, color=0xff0000):
"""Style the text from/to with a hotspot"""
send_scintilla =
#Use the scintilla low level messaging system to set the hotspot
self.SendScintilla(PyQt4.Qsci.QsciScintillaBase.SCI_STYLESETHOTSPOT, 2, True)
self.SendScintilla(PyQt4.Qsci.QsciScintillaBase.SCI_SETHOTSPOTACTIVEFORE, True, color)
self.SendScintilla(PyQt4.Qsci.QsciScintillaBase.SCI_SETHOTSPOTACTIVEUNDERLINE, True)
self.SendScintilla(PyQt4.Qsci.QsciScintillaBase.SCI_STARTSTYLING, index_from, 2)
self.SendScintilla(PyQt4.Qsci.QsciScintillaBase.SCI_SETSTYLING, length, 2)
Это делает текст в редакторе QScintilla кликабельным, когда вы наводите на него курсор мыши. Число 2 в вышеприведенных функциях является номером стиля горячей точки. Чтобы поймать событие, которое происходит, когда вы нажимаете горячую точку, подключитесь к этим сигналам:
QScintilla.SCN_HOTSPOTCLICK
QScintilla.SCN_HOTSPOTDOUBLECLICK
QScintilla.SCN_HOTSPOTRELEASECLICK
Для получения более подробной информации смотрите в документации точки доступа Scintilla: http://www.scintilla.org/ScintillaDoc.html и QScintilla горячих точек событий: http://pyqt.sourceforge.net/Docs/QScintilla2/classQsciScintillaBase.html
Прежде всего, большое спасибо господину Куковцу! У меня есть несколько вопросов относительно вашего ответа:
(1) Есть несколько вещей, которые я не понимаю в вашем примере функции.
def style_hotspot(self, index_from, length, color=0xff0000):
"""Style the text from/to with a hotspot"""
send_scintilla = # you undefine send_scintilla?
#Use the scintilla low level messaging system to set the hotspot
self.SendScintilla(..) # What object does 'self' refer to in this
self.SendScintilla(..) # context?
self.SendScintilla(..)
(2) Вы говорите:"Чтобы поймать событие, которое срабатывает при щелчке по горячей точке, подключитесь к следующим сигналам:"
QScintilla.SCN_HOTSPOTCLICK
QScintilla.SCN_HOTSPOTDOUBLECLICK
QScintilla.SCN_HOTSPOTRELEASECLICK
Как вы на самом деле подключаетесь к этим сигналам? Не могли бы вы привести один пример? Я привык к механизму слотов сигналов PyQt, но никогда не использовал его в QScintilla. Было бы очень полезно увидеть пример:-)
(3) Может быть, я что-то упустил, но я не вижу, где вы определяете в QScintilla, что функции и переменные (а не другие вещи) кликабельны в исходном коде?
Большое спасибо за вашу помощь:-)
Взгляните на следующую документацию:https://qscintilla.com/
Есть два способа сделать объекты интерактивными в Qscintilla - вы можете использовать горячие точки или индикаторы. Горячие точки требуют, чтобы вы переопределили поведение по умолчанию базового лексера, но я думаю, что индикаторы более удобны для вашего варианта использования.
Я предлагаю вам взглянуть на индикаторы, которые могут помочь вам сделать текст интерактивным, и вы можете определить обработчики событий, которые запускаются при нажатии на него.https://qscintilla.com/