[PyQt] Problem with folding and QSciScintilla

projetmbc projetmbc at club-internet.fr
Mon Jul 27 22:46:36 BST 2009


Baz Walter a écrit :
> projetmbc wrote:
>> Baz Walter a écrit :
>>> hello christophe
>>>
>>> i expanded my earlier example to include folding. this is a complete 
>>> config files lexer based on the lexer for properties files in 
>>> src/LexOthers.cpp.
>>>
>> Thanks for this. Santa Claus exists... ;-)
>>
>> I'll look at and study your code to see how to do my simple lexer.
>>
>> Did you allow me to use your code in a future free tutorial on the 
>> use of QScintilla ? I'll cite your name.
>>
>
> your welcome. feel free to use it as you wish
>
> regards
> baz walter
Finally I've done a very simple lexer that find multi-lines /*...*/ wich 
can be "folded" and single line //... comment of C++ kind. I hope that 
will help people and that there is no bug.

Thanks for all the informations. The next step would be to highlight 
some specific words but the harder has been done.

Christophe.
-------------- next part --------------
import sys
from PyQt4.QtCore import SIGNAL, SLOT, QString
from PyQt4.QtGui import QApplication, QMainWindow, QColor, QFont
from PyQt4.Qsci import QsciScintilla, QsciLexerCustom

# The most important and hard part of this code was given by Baz WALTER on the PyQt list.

if sys.hexversion < 0x020600F0:
    print('python 2.6 or greater is required by this program')
    sys.exit(1)

_sample = """
/* This example uses
this multi-line coment
which can be extanded or retracted.

The end must be the following line.
*/

A text outside a MultiLinesComment
is not retactable.


/* Another

test.
*/
"""


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle('Custom Lexer For Config Files')
        self.setGeometry(50, 200, 400, 400)
        self.editor = QsciScintilla(self)
        self.editor.setUtf8(True)

# + OR - TO FOLD OR UNFOLD
        self.editor.setFolding(QsciScintilla.BoxedTreeFoldStyle)
# LINES' NUMBER IN THE MARGIN
        self.editor.setMarginLineNumbers(1,True)
        self.editor.setMarginWidth(1, QString("-------"))
                # OK for 3 digits. This was found by direct tests...

        self.setCentralWidget(self.editor)
        self.lexer = ConfigLexer(self.editor)
        self.editor.setLexer(self.lexer)
        self.editor.setText(_sample)


class ConfigLexer(QsciLexerCustom):
    def __init__(self, parent):
        QsciLexerCustom.__init__(self, parent)
        self._styles = {
            0: 'Default',
            1: 'MultiLinesComment_Start',
            2: 'MultiLinesComment',
            3: 'MultiLinesComment_End',
            4: 'SingleLineComment'
            }
        for key,value in self._styles.iteritems():
            setattr(self, value, key)
        self._foldcompact = True
        self.__comment = [self.MultiLinesComment,
                          self.MultiLinesComment_End,
                          self.MultiLinesComment_Start,
                          self.SingleLineComment]

    def foldCompact(self):
        return self._foldcompact

    def setFoldCompact(self, enable):
        self._foldcompact = bool(enable)

    def language(self):
        return 'Config Files'

    def description(self, style):
        return self._styles.get(style, '')

    def defaultColor(self, style):
        if style == self.Default:
            return QColor('#000000')
        elif style in self.__comment:
            return QColor('#A0A0A0')
        return QsciLexerCustom.defaultColor(self, style)

    def defaultFont(self, style):
        if style in self.__comment:
            if sys.platform in ('win32', 'cygwin'):
                return QFont('Comic Sans MS', 9, QFont.Bold)
            return QFont('Bitstream Vera Serif', 9, QFont.Bold)
        return QsciLexerCustom.defaultFont(self, style)

    def defaultPaper(self, style):
# Here we change the color of the background.
# We want to colorize all the background of the line.
# This is done by using the following method defaultEolFill() .
        if style in self.__comment:
            return QColor('#FFEECC')
        return QsciLexerCustom.defaultPaper(self, style)

    def defaultEolFill(self, style):
# This allowed to colorize all the background of a line.
        if style in self.__comment:
            return True
        return QsciLexerCustom.defaultEolFill(self, style)

    def styleText(self, start, end):
        editor = self.editor()
        if editor is None:
            return

        SCI = editor.SendScintilla
        GETFOLDLEVEL = QsciScintilla.SCI_GETFOLDLEVEL
        SETFOLDLEVEL = QsciScintilla.SCI_SETFOLDLEVEL
        HEADERFLAG = QsciScintilla.SC_FOLDLEVELHEADERFLAG
        LEVELBASE = QsciScintilla.SC_FOLDLEVELBASE
        NUMBERMASK = QsciScintilla.SC_FOLDLEVELNUMBERMASK
        WHITEFLAG = QsciScintilla.SC_FOLDLEVELWHITEFLAG
        set_style = self.setStyling

        source = ''
        if end > editor.length():
            end = editor.length()
        if end > start:
            source = bytearray(end - start)
            SCI(QsciScintilla.SCI_GETTEXTRANGE, start, end, source)
        if not source:
            return

        compact = self.foldCompact()

        index = SCI(QsciScintilla.SCI_LINEFROMPOSITION, start)
        if index > 0:
            pos = SCI(QsciScintilla.SCI_GETLINEENDPOSITION, index - 1)
            prevState = SCI(QsciScintilla.SCI_GETSTYLEAT, pos)
        else:
            prevState = self.Default

        self.startStyling(start, 0x1f)

        for line in source.splitlines(True):
# Try to uncomment the following line to see in the console
# how Scintiallla works. You have to think in terms of isolated
# lines rather than globally on the whole text.
#            print line

            length = len(line)
# We must take care of empty lines.
# This is done here.
            if length == 1:
                if prevState == self.MultiLinesComment or prevState == self.MultiLinesComment_Start:
                    newState = self.MultiLinesComment
                else:
                    newState = self.Default
# We work with a non empty line.
            else:
                if line.startswith('/*'):
                    newState = self.MultiLinesComment_Start
                elif line.startswith('*/'):
                    if prevState == self.MultiLinesComment or prevState == self.MultiLinesComment_Start:
                        newState = self.MultiLinesComment_End
                    else:
                        newState = self.Default
                elif line.startswith('//'):
                    if prevState == self.MultiLinesComment or prevState == self.MultiLinesComment_Start:
                        newState = self.MultiLinesComment
                    else:
                        newState = self.SingleLineComment
                elif prevState == self.MultiLinesComment or prevState == self.MultiLinesComment_Start:
                    newState = self.MultiLinesComment
                else:              
                    newState = self.Default

            set_style(length, newState)

# Definition of the folding.
# Documentation : http://scintilla.sourceforge.net/ScintillaDoc.html#Folding
            if newState == self.MultiLinesComment_Start:
                if prevState == self.MultiLinesComment:
                    level = LEVELBASE + 1
                else:
                    level = LEVELBASE | HEADERFLAG
            elif newState == self.MultiLinesComment or newState == self.MultiLinesComment_End:
                level = LEVELBASE + 1
            else:
                level = LEVELBASE

            SCI(SETFOLDLEVEL, index, level)

            pos = SCI(QsciScintilla.SCI_GETLINEENDPOSITION, index)
            prevState = SCI(QsciScintilla.SCI_GETSTYLEAT, pos)

            index += 1


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.connect(app, SIGNAL('lastWindowClosed()'), app, SLOT('quit()'))
    win = MainWindow()
    win.show()
    sys.exit(app.exec_()) 


More information about the PyQt mailing list