DocumentDemo Example

from DevMachines import __pyside2__, __pyside6__

if __pyside2__:
    from PySide2 import QtCore
    from PySide2.QtCore import Qt, QRegularExpression, QFile, QFileInfo, QTextStream
    from PySide2.QtGui import QSyntaxHighlighter, QTextCharFormat, QFont
    from PySide2.QtWidgets import QTextEdit, QFileDialog, QApplication

if __pyside6__:
    from PySide6 import QtCore
    from PySide6.QtCore import Qt, QRegularExpression, QFile, QFileInfo, QTextStream
    from PySide6.QtGui import QSyntaxHighlighter, QTextCharFormat, QFont
    from PySide6.QtWidgets import QTextEdit, QFileDialog, QApplication

class HighlightingRule():
    def __init__(self):
        self.pattern = None
        self.format = None

class Highlighter(QSyntaxHighlighter):
    def __init__(self, parent):
        QSyntaxHighlighter.__init__(self, parent)

        self.highlightingRules = []

        self.keywordFormat = QTextCharFormat()
        self.classFormat = QTextCharFormat()
        self.singleLineCommentFormat = QTextCharFormat()
        self.multiLineCommentFormat = QTextCharFormat()
        self.quotationFormat = QTextCharFormat()
        self.functionFormat = QTextCharFormat()

        self.keywordFormat.setForeground(Qt.darkBlue)
        self.keywordFormat.setFontWeight(QFont.Bold)
        keywordPatterns = [
            "\\bchar\\b", "\\bclass\\b", "\\bconst\\b",
            "\\bdouble\\b", "\\benum\\b", "\\bexplicit\\b",
            "\\bfriend\\b", "\\binline\\b", "\\bint\\b"
            "\\blong\\b", "\\bnamespace\\b", "\\boperator\\b",
            "\\bprivate\\b", "\\bprotected\\b", "\\bpublic\\b",
            "\\bshort\\b", "\\bsignals\\b", "\\bsigned\\b",
            "\\bslots\\b", "\\bstatic\\b", "\\bstruct\\b",
            "\\btemplate\\b", "\\btypedef\\b", "\\btypename\\b",
            "\\bunion\\b", "\\bunsigned\\b", "\\bvirtual\\b"
            "\\bvoid\\b", "\\bvolatile\\b"]

        for pattern in keywordPatterns:
            rule = HighlightingRule()
            rule.pattern = QRegularExpression(pattern)
            rule.format = self.keywordFormat
            self.highlightingRules.append(rule)

        self.classFormat.setFontWeight(QFont.Bold)
        self.classFormat.setForeground(Qt.darkMagenta)
        rule.pattern = QRegularExpression("\\bQ[A-Za-z]+\\b")
        rule.format = self.classFormat
        self.highlightingRules.append(rule)

        self.singleLineCommentFormat.setForeground(Qt.red)
        rule.pattern = QRegularExpression("//[^\n]*")
        rule.format = self.singleLineCommentFormat
        self.highlightingRules.append(rule)

        self.multiLineCommentFormat.setForeground(Qt.darkGreen)

        self.quotationFormat.setForeground(Qt.darkGreen)
        rule.pattern = QRegularExpression("\".*\"")
        rule.format = self.quotationFormat
        self.highlightingRules.append(rule)

    #    self.functionFormat.setFontItalic(True)
        self.functionFormat.setForeground(Qt.blue)
        rule.pattern = QRegularExpression("\\b[A-Za-z0-9_]+(?=\\()")
        rule.format = self.functionFormat
        self.highlightingRules.append(rule)

        self.commentStartExpression = QRegularExpression("/\\*")
        self.commentEndExpression = QRegularExpression("\\*/")

    def highlightBlock(self, text):
        for rule in self.highlightingRules:
            expression = rule.pattern
            matchIterator = expression.globalMatch(text)
            while matchIterator.hasNext():
                match = matchIterator.next()
                self.setFormat(match.capturedStart(), match.capturedLength(), rule.format)

        self.setCurrentBlockState(0)

        startIndex = 0
        if self.previousBlockState() != 1:
            startIndex = self.commentStartExpression.match(text).capturedStart()

        while startIndex >= 0:
            match = self.commentEndExpression.match(text, startIndex)
            endIndex = match.capturedStart()
            commentLength = 0
            if endIndex == -1:
                self.setCurrentBlockState(1)
                commentLength = len(text) - startIndex
            else:
                commentLength = endIndex - startIndex + match.capturedLength()

            self.setFormat(startIndex, commentLength, self.multiLineCommentFormat)

            startIndex = self.commentStartExpression.match(text, startIndex + commentLength).capturedStart()

class MdiChild(QTextEdit):
    sequenceNumber = 1
    def __init__(self):
        QTextEdit.__init__(self)
        self.currentFileName = ""
        self.highlighter = Highlighter(self.document())

        font = QFont()
        font.setFamily("Courier")
        font.setFixedPitch(True)
        font.setPointSize(10)
        self.setFont(font)

        self.untitled = True
        self.connect(self.document(), QtCore.SIGNAL("modificationChanged(bool)"), self, QtCore.SLOT("updateDocumentModified(bool)"))
        self.setAttribute(Qt.WA_DeleteOnClose)

    def newFile(self):
        self.untitled = True
        self.sequenceNumber = self.sequenceNumber + 1
        self.setCurrentFileName("Source{0}.cpp".format(self.sequenceNumber), False)

    def loadFile(self, fileName):
        file = QFile(fileName)
        if not file.open(QFile.ReadOnly | QFile.Text):
            QMessageBox.warning(self, self.tr("MDI"), "Cannot read file {0}:\n{1}.".format(fileName, file.errorString()))
            return False

        stream = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.setPlainText(stream.readAll())
        QApplication.restoreOverrideCursor()

        self.setCurrentFileName(fileName, True)
        return True

    def save(self):
        if self.untitled:
            return self.saveAs()
        else:
            return self.saveFile(self.currentFileName)

    def saveAs(self):
        fileName = QFileDialog.getSaveFileName(self, self.tr("Save As"), self.currentFileName)[0]
        if fileName == "":
            return False
        return saveFile(fileName)

    def saveFile(self, fileName):
        file = QFile(fileName)
        if not file.open(QFile.WriteOnly | QFile.Text):
            QMessageBox.warning(self, self.tr("MDI"), "Cannot write file {0}:\n{1}.".format(QDir.toNativeSeparators(fileName), file.errorString()))
            return False

        out = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        out << self.toPlainText()
        QApplication.restoreOverrideCursor()

        setCurrentFileName(fileName, True)
        return True

    def userFriendlyCurrentFile(self):
        return QFileInfo(self.currentFileName).fileName()

    def closeEvent(self, event):
        if self.aboutToBeClosed():
            event.accept()
        else:
            event.ignore()

    def updateDocumentModified(self, modified):
        title = self.userFriendlyCurrentFile()
        if modified:
            title = title + "[*]"
        self.setWindowTitle(title);
        self.setWindowModified(modified)

    def aboutToBeClosed(self):
        if not self.document().isModified():
            return True

        ret = QMessageBox.warning(self, self.tr("MDI"), "'{0}' has been modified.\n Do you want to save your changes?".format(self.userFriendlyCurrentFile()),
            QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
        if ret == QMessageBox.Save:
            return self.save()
        elif ret == QMessageBox.Cancel:
            return False
        return True

    def currentFileName(self):
        return self.currentFileName

    def setCurrentFileName(self, fileName, titled):
        if titled:
            self.currentFileName = QFileInfo(fileName).canonicalFilePath()
        else:
            self.currentFileName = fileName
        self.untitled = titled
        self.updateDocumentModified(self.document().isModified())