2016-09-25 4 views
1

J'ai eu ce petit code mcve:Comment remplacer correctement certaines correspondances sur un widget QScintilla?

import sys 
import re 

from PyQt5 import QtGui, QtWidgets, QtCore 
from PyQt5.QtCore import Qt 
from PyQt5.Qsci import QsciScintilla 
from PyQt5 import Qsci 


class FloatSlider(QtWidgets.QWidget): 

    value_changed = QtCore.pyqtSignal(float) 

    def __init__(self, value=0.0, parent=None): 
     super().__init__(parent) 

     self.slider = QtWidgets.QSlider(Qt.Horizontal) 
     self.label = QtWidgets.QLabel() 
     self.label.setAlignment(Qt.AlignCenter) 

     self.adjust(value) 

     layout = QtWidgets.QHBoxLayout() 
     layout.addWidget(self.slider) 
     layout.addWidget(self.label) 

     self.slider.valueChanged.connect(self.on_value_changed) 
     self.setLayout(layout) 
     self.setWindowTitle("Adjust number") 

    def adjust(self, value): 
     width = 100 # TODO: Adjust it properly depending on input value 
     self.slider.setRange(value - width, value + width) 
     self.slider.setSingleStep(1) 
     self.slider.setValue(value) 
     self.label.setText(str(float(value))) 

    def on_value_changed(self, value): 
     # vmax, vmin = self.slider.minimum(), self.slider.maximum() 
     # value = 2 * value/(vmax - vmin) 
     self.label.setText(str(float(value))) 
     self.value_changed.emit(value) 


class SimpleEditor(QsciScintilla): 

    def __init__(self, language=None, parent=None): 
     super().__init__(parent) 

     self.slider = FloatSlider(value=0.0) 
     self.slider.value_changed.connect(self.float_value_changed) 

     font = QtGui.QFont() 
     font.setFamily('Courier') 
     font.setFixedPitch(True) 
     font.setPointSize(10) 
     self.setFont(font) 
     self.setMarginsFont(font) 
     fontmetrics = QtGui.QFontMetrics(font) 
     self.setMarginsFont(font) 
     self.setMarginWidth(0, fontmetrics.width("00000") + 6) 
     self.setMarginLineNumbers(0, True) 
     self.setMarginsBackgroundColor(QtGui.QColor("#cccccc")) 
     self.setBraceMatching(QsciScintilla.SloppyBraceMatch) 
     self.setCaretLineVisible(True) 
     self.setCaretLineBackgroundColor(QtGui.QColor("#E8E8FF")) 

     if language: 
      self.lexer = getattr(Qsci, 'QsciLexer' + language)() 
      self.setLexer(self.lexer) 

     self.SendScintilla(QsciScintilla.SCI_FOLDALL, True) 
     self.setAutoCompletionThreshold(1) 
     self.setAutoCompletionSource(QsciScintilla.AcsAPIs) 
     self.setFolding(QsciScintilla.BoxedTreeFoldStyle) 

     # Signals/Slots 
     self.cursorPositionChanged.connect(self.on_cursor_position_changed) 
     self.copyAvailable.connect(self.on_copy_available) 
     self.indicatorClicked.connect(self.on_indicator_clicked) 
     self.indicatorReleased.connect(self.on_indicator_released) 
     self.linesChanged.connect(self.on_lines_changed) 
     self.marginClicked.connect(self.on_margin_clicked) 
     self.modificationAttempted.connect(self.on_modification_attempted) 
     self.modificationChanged.connect(self.on_modification_changed) 
     self.selectionChanged.connect(self.on_selection_changed) 
     self.textChanged.connect(self.on_text_changed) 
     self.userListActivated.connect(self.on_user_list_activated) 

    def float_value_changed(self, v): 
     print(v) 

    def on_cursor_position_changed(self, line, index): 
     text = self.text(line) 
     for match in re.finditer('(?:^|(?<=\W))\d+(?:\.\d+)?(?=$|\W)', text): 
      start, end = match.span() 
      if start <= index <= end: 
       pos = self.positionFromLineIndex(line, start) 
       x = self.SendScintilla(
        QsciScintilla.SCI_POINTXFROMPOSITION, 0, pos) 
       y = self.SendScintilla(
        QsciScintilla.SCI_POINTYFROMPOSITION, 0, pos) 
       point = self.mapToGlobal(QtCore.QPoint(x, y)) 
       num = float(match.group()) 
       message = 'number: %s' % num 

       self.slider.setWindowTitle('line: {0}'.format(line)) 
       self.slider.adjust(num) 
       self.slider.move(point + QtCore.QPoint(0, 20)) 
       self.slider.show() 

       break 

    def on_copy_available(self, yes): 
     print('-' * 80) 
     print("on_copy_available") 

    def on_indicator_clicked(self, line, index, state): 
     print("on_indicator_clicked") 

    def on_indicator_released(self, line, index, state): 
     print("on_indicator_released") 

    def on_lines_changed(self): 
     print("on_lines_changed") 

    def on_margin_clicked(self, margin, line, state): 
     print("on_margin_clicked") 

    def on_modification_attempted(self): 
     print("on_modification_attempted") 

    def on_modification_changed(self): 
     print("on_modification_changed") 

    def on_selection_changed(self): 
     print("on_selection_changed") 

    def on_text_changed(self): 
     print("on_text_changed") 

    def on_user_list_activated(self, id, text): 
     print("on_user_list_activated") 


def show_requirements(): 
    print(sys.version) 
    print(QtCore.QT_VERSION_STR) 
    print(QtCore.PYQT_VERSION_STR) 

if __name__ == "__main__": 
    show_requirements() 

    app = QtWidgets.QApplication(sys.argv) 

    ex = QtWidgets.QWidget() 
    hlayout = QtWidgets.QHBoxLayout() 
    ed = SimpleEditor("JavaScript") 

    hlayout.addWidget(ed) 

    ed.setText("""#ifdef GL_ES 
precision mediump float; 
#endif 

#extension GL_OES_standard_derivatives : enable 

uniform float time; 
uniform vec2 mouse; 
uniform vec2 resolution; 

void main(void) { 

    vec2 st = (gl_FragCoord.xy/resolution.xy); 
    vec2 lefbot = step(vec2(0.1), st); 
    float pct = lefbot.x*lefbot.y; 
    vec2 rigtop = step(vec2(0.1), 1.-st); 
    pct *= rigtop.x*rigtop.y; 
    vec3 color = vec3(pct); 

    gl_FragColor = vec4(color, 1.0);""") 

    ex.setLayout(hlayout) 
    ex.show() 
    ex.resize(800, 600) 

    sys.exit(app.exec_()) 

Il y a plusieurs questions que je ne sais pas comment répondre:

  • Mon widget curseur change la largeur widget à chaque fois que je change les valeurs, je essayé addStrecht(1) mais cela n'a pas fonctionné comme je l'ai prévu car il y avait trop d'espace vide entre les widgets (ie: layout arrangement -> slider | strecht | label)
  • Une fois que je tape une valeur numérique sur le widget QScintilla le widget FloatSlider va apparaître et c'est certainement quelque chose que je ne fais pas vouloir. Je voudrais qu'il n'apparaisse que lorsque j'appuie sur une telle valeur numérique avec le bouton gauche de la souris ou toute autre combinaison (ie: ctrl + left_mouse)
  • Je ne sais pas comment remplacer correctement le texte QScintilla (regex match) sur temps réél. Idéalement, seul le texte correspondant à QScintilla devrait être modifié, par exemple, je ne veux pas remplacer tout le texte parce que l'effet visuel serait assez effrayant.

Il n'a pas eu l'impression d'ouvrir 3 questions différentes pour ces petites Les doutes étaient corrects alors j'ai décidé de les rassembler dans le même fil. Espérons que ce soit OK

+0

Le fait que vous appelez ces "petits doutes" me porte à penser que vous sous-estimez massivement la complexité de cette tâche. Sur le deuxième point - vous voulez vraiment ** que ** le curseur reste visible. Si vous essayez l'exemple de [khan academy] (http://www.khanacademy.org/computer-programming/tree-generator/822944839), vous remarquerez que le gizmo reste visible pendant que vous tapez. C'est ainsi que fonctionnent tous les auto-finaliseurs, les conseils d'appel, etc. Je ne veux pas vous décourager, mais il faudra * beaucoup * d'efforts pour que cela fonctionne correctement - en particulier la manipulation du clavier. – ekhumoro

+0

@ekhumoro Certainement je ne sous-estime pas la création de ce widget cool :). Cela implique plusieurs choses: 1) la création d'un bon parser glsl, j'ai quelques problèmes à faire fonctionner ce [un] (https://github.com/nicholasbishop/pyglsl_parser) et ce [un] (https: // github .com/rougier/glsl-parser) 2) Maîtriser les possibilités du widget qscintilla (comme vous pouvez le voir je suis loin de le maîtriser) 3) Créer des widgets appropriés pour gérer les types 1/2/3d glsl, je vais créer des widgets pyqt similaires à ceux fournis [ici] (http://editor.thebookofshaders.com/). En tout cas, un petit problème à chaque fois :) – BPL

Répondre

0
  • Utilisez setFixedWidth pour éviter soit le curseur ou étiquette changer la largeur sur chaque changement
  • Vous ne devriez pas utiliser événement on_cursor_position_changed, au lieu simplement utiliser mouseReleaseEvent
  • Une bonne façon de remplacer certains matches à certaine position serait au lieu d'utiliser setText en utilisant les méthodes insertAt et SCI_DELETERANGE. Pour plus d'informations, consultez QScintilla docs
1

Sur le deuxième point: Je pense que vous devriez abandonner l'idée d'avoir une fenêtre/boîte de dialogue séparée pour le curseur. Il devrait plutôt s'agir d'une fenêtre contextuelle qui reste au-dessus de l'éditeur jusqu'à ce que vous cliquiez en dehors de celle-ci ou que vous appuyez sur Echap (c'est-à-dire comme une info-bulle ou un menu contextuel).

Pour vous donner une idée de la façon dont cela pourrait regarder (mais sans chercher à résoudre l'un des autres problèmes potentiels), essayez quelque chose comme ceci:

class FloatSlider(QtWidgets.QFrame):  
    value_changed = QtCore.pyqtSignal(float) 

    def __init__(self, value=0.0): 
     super().__init__() 
     ...  
     self.setFrameShape(QtWidgets.QFrame.Box) 
     self.setFrameShadow(QtWidgets.QFrame.Plain) 
     self.setParent(None, QtCore.Qt.Popup) 
     self.setFocusPolicy(QtCore.Qt.NoFocus) 

    def adjust(self, value): 
     ... 
     self.slider.setFocus()