2016-10-12 1 views
2

J'essaye de faire un simple texteditor avec la coloration syntaxique de base, l'achèvement de code et les fonctions cliquables & variables dans PyQt5. Mon meilleur espoir pour y parvenir est d'utiliser le port QScintilla
pour PyQt5.
J'ai trouvé l'exemple texteditor suivant QScintilla sur le site Web d'Eli Bendersky (http://eli.thegreenplace.net/2011/04/01/sample-using-qscintilla-with-pyqt, Victor S. l'a adapté à PyQt5). Je pense que cet exemple est un bon point de départ:Editeur de texte basé sur QScintilla dans PyQt5 avec des fonctions et des variables cliquables

#------------------------------------------------------------------------- 
# qsci_simple_pythoneditor.pyw 
# 
# QScintilla sample with PyQt 
# 
# Eli Bendersky ([email protected]) 
# 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_() 

Il suffit de copier-coller ce code dans un fichier .py vide, et l'exécuter. Vous devriez obtenir l'éditeur de texte simple suivant apparaît sur votre écran:

enter image description here

Remarquez comment parfait la coloration syntaxique est! QScintilla a certainement fait un peu d'analyse en arrière-plan pour y parvenir.
Est-il possible de faire fonctions cliquables & variables pour cette entrée de texte? Tout IDE qui se respecte l'a. Vous cliquez sur une fonction et l'IDE passe à la définition de la fonction. La même chose pour les variables. Je voudrais savoir:

  • Est-ce que qscintilla soutien fonctions cliquables & les variables?
  • Si ce n'est pas le cas, est-il possible d'importer un autre module python qui implémente cette fonctionnalité dans le logiciel de création de comptes QScintilla?

EDIT:
λuser noté ce qui suit:

noms de fonction cliquables exigent l'analyse complète avec une connaissance beaucoup plus approfondie d'un langage de programmation [..]
C'est bien au-delà la portée de Scintilla/QScintilla. Scintilla fournit un moyen de réagir lorsque la souris clique quelque part sur le texte, mais la logique de "où est la définition d'une fonction" n'est pas dans Scintilla et ne le sera probablement jamais.
Cependant, certains projets sont dédiés à cette tâche, comme ctags. Vous pouvez simplement écrire un wrapper autour de ce type d'outil.

je suppose que l'écriture comme emballage pour ctags est maintenant sur ma liste TODO. La toute première étape consiste à obtenir une réaction (signal Qt) lorsque l'utilisateur clique sur une fonction ou une variable. Et peut-être que la fonction/variable devrait devenir un peu bleutée lorsque vous passez la souris dessus, pour avertir l'utilisateur qu'il est cliquable. J'ai déjà essayé d'y parvenir, mais je suis retenu par la pénurie de documentation QScintilla.

Alors laissez-nous couperons la question: Comment faites-vous une fonction ou variable dans la cliquable texteditor qscintilla (avec cliquable défini comme 'quelque chose se passe')


EDIT:
Je viens de revenir à cette question maintenant - plusieurs mois plus tard. J'ai coopéré avec mon ami Matic Kukovec pour concevoir un site Web sur QScintilla. Il est un tutoriel débutant convivial sur la façon de l'utiliser:

enter image description here

https://qscintilla.com/

J'espère que cette initiative comble le vide de la documentation manque.

+0

Il y a deux questions distinctes ici. Le premier problème est de cliquer sur le texte et d'identifier le symbole à une position donnée. Ceci est pris en charge dans la mesure où toutes les fonctionnalités de bas niveau existent dans QScintilla/Scintilla pour que vous puissiez écrire l'implémentation vous-même. Le deuxième problème est de lier des symboles à des définitions, ce qui est une caractéristique normalement trouvée dans les IDE plutôt que "un simple éditeur de texte". Il n'y a pas de support direct pour cela, et même si vous utilisez quelque chose comme 'ctags', il va encore y avoir beaucoup de travail incorporant dans un éditeur de texte. – ekhumoro

+0

Salut @ekhumoro, merci beaucoup pour votre aide. Je n'ai pas peur de beaucoup de travail, tant que je commence quelque part. Ce qui me retient en ce moment, c'est rendre les fonctions et les variables «cliquables» dans le sens où «quelque chose se passe». Une fois cela obtenu, je peux continuer à faire un wrapper pour 'ctags'. Avez-vous une idée sur la façon de faire cette première étape? La documentation QScintilla est très pauvre ... –

+0

Je pense que vous devriez viser plus bas pour commencer. Configurez un [gestionnaire de menu contextuel personnalisé] (http://doc.qt.io/qt-5/qwidget.html#customContextMenuRequested), puis essayez quelque chose comme [wordAtPoint] (http://pyqt.sourceforge.net/ Docs/QScintilla2/classQsciScintilla.html # af6ff6d63f13aab28979985135203df70) pour obtenir le symbole au 'QPoint' donné. (PS: la façon de faire de la documentation est de lire d'abord les docs de Scintilla, puis de revenir aux docs de QScintilla pour voir quelles sont les API de haut niveau fournies. /SciTE.html) sont aussi parfois utiles). – ekhumoro

Répondre

1

Une façon d'utiliser Pyqt5 avec option avec des fonctions et des variables cliquables. Votre script dont la partie cliquable a été créée, ressemblerait à ceci dans PyQt5 avec un signal personnalisé.

PyQt4 SIGNAL

self.connect(self,SIGNAL('marginClicked(int, int, Qt::KeyboardModifiers)'), 
self.on_margin_clicked) 

PyQt5 SIGNAL

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_() 
2

La mise en évidence de la syntaxe consiste simplement à exécuter une lexeur sur le fichier source pour trouver des jetons, puis lui attribuer des styles. Un lexer a une compréhension très basique d'un langage de programmation, il ne comprend que ce qu'est un littéral numérique, un mot-clé, un opérateur, un commentaire, quelques autres et c'est tout. C'est un travail un peu simple qui peut être effectué avec seulement des expressions régulières. D'autre part, les noms de fonctions cliquables nécessitent une analyse complète avec une connaissance beaucoup plus approfondie d'un langage de programmation, par ex. est-ce une déclaration d'une variable ou d'une utilisation, etc. En outre, cela peut nécessiter l'analyse d'autres fichiers source non ouverts par l'éditeur actuel.

Ceci est bien au-delà de la portée de Scintilla/QScintilla. Scintilla fournit un moyen de réagir lorsque la souris clique quelque part sur le texte, mais la logique de "où est la définition d'une fonction" n'est pas dans Scintilla et ne le sera probablement jamais.

Cependant, certains projets sont dédiés à cette tâche, comme ctags. Vous pouvez simplement écrire un wrapper autour de ce type d'outil.

+0

Merci beaucoup pour votre commentaire intéressant. Je suppose que l'écriture d'un tel wrapper nécessite au moins qu'une fonction ou une variable soit 'cliquable' dans le texteditor de QScintilla. Comment faites-vous un mot cliquable? –

1

J'ai reçu une réponse utile de Matic Kukovec par courrier, que j'aimerais partager ici. Matic Kukovec a fait un IDE incroyable basé sur QScintilla: https://github.com/matkuki/ExCo. Peut-être que cela incitera plus de gens à approfondir QScintilla (et les variables et fonctions cliquables).


Les zones sensibles rendent le texte cliquable. Vous devez le styliser manuellement en utilisant la fonction QScintilla.SendScintilla. Exemple de fonction que j'ai utilisé dans mon éditeur 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) 

Cela rend le texte dans l'éditeur qscintilla cliquable lorsque vous passez la souris dessus. Le numéro 2 dans les fonctions ci-dessus est le numéro de style de point d'accès. Pour attraper l'événement qui se déclenche lorsque vous cliquez sur le point d'accès, se connecter à ces signaux:

QScintilla.SCN_HOTSPOTCLICK 
QScintilla.SCN_HOTSPOTDOUBLECLICK 
QScintilla.SCN_HOTSPOTRELEASECLICK 

Pour plus de détails regarder la documentation hotspot Scintilla: http://www.scintilla.org/ScintillaDoc.html#SCI_STYLESETHOTSPOT et événements hotspot qscintilla: http://pyqt.sourceforge.net/Docs/QScintilla2/classQsciScintillaBase.html#a5eff383e6fa96cbbaba6a2558b076c0b


Tout d'abord, un grand merci à M. Kukovec!J'ai quelques questions concernant votre réponse:

(1) Il y a quelques choses que je ne comprends pas dans votre exemple de fonction.

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) Vous dites « Pour attraper l'événement qui se déclenche lorsque vous cliquez sur le hotspot, se connecter à ces signaux: »

QScintilla.SCN_HOTSPOTCLICK 
QScintilla.SCN_HOTSPOTDOUBLECLICK 
QScintilla.SCN_HOTSPOTRELEASECLICK 

Comment vous connecter réellement à ces signaux? Pourriez-vous donner un exemple? Je suis habitué au mécanisme de slot de signal de PyQt, mais je ne l'ai jamais utilisé sur QScintilla. Il serait peut-être utile de voir un exemple :-)

(3) Peut-être que j'ai manqué quelque chose, mais je ne vois pas où vous définissez dans QScintilla que les fonctions et les variables (et pas d'autres choses) sont cliquables dans le code source?

Merci beaucoup pour votre aide :-) genre