2016-09-25 2 views
1

Я получил этот маленький код mcve:Как правильно заменить определенные матчи в виджетах 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_()) 

Есть несколько вопросов, я не знаю, как обращаться:

  • Мой слайдер виджет изменения ширины виджета, каждый раз, когда я изменить значения, я попробовал addStrecht(1), но это не сработало, поскольку я предполагал, что между виджетами было слишком много свободного места (то есть: расположение компоновки -> слайдер | стречт | метка)
  • После ввода числового значения в виджет QScintilla виджет FloatSlider будет появляются, и это определенно то, что я делаю не хочу. Я бы хотел, чтобы он отображался только тогда, когда я нажимаю такое числовое значение левой кнопкой мыши или любой другой комбинацией (например: ctrl + left_mouse).
  • Я не знаю, как правильно заменить текст QScintilla (совпадение регулярных выражений) на в режиме реального времени. В идеале должен быть изменен только текст, совместимый с QScintilla, например, я не хочу заменять весь текст, потому что визуальный эффект был бы довольно жутким.

Не хотелось открывать 3 разных вопроса для этих маленьких сомнения были в порядке, поэтому я решил собрать их в одной теме. Надеюсь, это нормально.

+0

Тот факт, что вы называете эти «маленькие сомнения», заставляет меня подозревать, что вы значительно недооцениваете сложность этой задачи. На втором пункте - вы определенно ** сделаете ** хотите, чтобы ползунок оставался видимым. Если вы попробуете [академический пример хан] (http://www.khanacademy.org/computer-programming/tree-generator/822944839), вы заметите, что гизмо остается видимым во время ввода. Вот как работают все автодоставки, подсказки и т. Д. Я не хочу отговаривать вас, но это потребует много усилий, чтобы добиться этой работы, особенно при работе с клавиатурой. – ekhumoro

+0

@ekhumoro Определенно, я не недооцениваю создание этого классного виджета :). Это включает в себя несколько вещей: 1) создание хорошего анализатора glsl, у меня возникают некоторые проблемы, делающие работу этим [one] (https://github.com/nicholasbishop/pyglsl_parser), и этот [один] (https: // github .com/rougier/glsl-parser) 2) освоение возможностей виджета qscintilla (так как вы можете видеть, что я далеко от его освоения). 3) Создание правильных виджетов для обработки 1/2/3D типов glsl, я буду создавать аналогичные виджеты pyqt для тех, которые предоставлены [здесь] (http://editor.thebookofshaders.com/). В любом случае, одна маленькая проблема каждый раз :) – BPL

ответ

0
  • Использование setFixedWidth избежать либо ползунок или этикетки изменения ширины при каждом изменении
  • Вы не должны использовать on_cursor_position_changed событие, а не просто использовать mouseReleaseEvent
  • хороший способ заменить некоторые матчи на определенной позиции, будет вместо использования setText с использованием методов insertAt и SCI_DELETERANGE. Для получения дополнительной информации проверьте QScintilla docs
1

Во втором пункте: я думаю, вы должны отказаться от идеи иметь отдельное окно/диалог для слайдера. Вместо этого это будет всплывающее окно, которое остается поверх редактора, пока вы не выйдете за его пределы или не отпустите клавишу (например, как всплывающая подсказка или контекстное меню).

Чтобы дать вам представление о том, как это может выглядеть (но без попытки решить какой-либо из других потенциальных проблем), попробовать что-то вроде этого:

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() 
Смежные вопросы