Jan-09-2018, 06:56 PM
based on the PyQt5 'RichText' example.
I added a lot of features.
(you can also use it as an HTML editor)
code on github
I added a lot of features.
(you can also use it as an HTML editor)
code on github
#!/usr/bin/python3 # -- coding: utf-8 -- from PyQt5.QtWidgets import QTextEdit, QWidget, QVBoxLayout, QApplication, QFileDialog, QMessageBox, QHBoxLayout, \ QToolBar, QComboBox, QAction, QLineEdit, QMenu, QMainWindow, QActionGroup, \ QFontComboBox, QColorDialog, QInputDialog, QPushButton from PyQt5.QtGui import QIcon, QPainter, QTextFormat, QColor, QTextCursor, QKeySequence, QClipboard, \ QTextCharFormat, QTextCharFormat, QFont, QPixmap, QFontDatabase, QFontInfo, QTextDocumentWriter, \ QImage, QTextListFormat, QTextBlockFormat, QTextDocumentFragment from PyQt5.QtCore import Qt, QDir, QFile, QFileInfo, QTextStream, QSettings, QTextCodec, QSize, QMimeData, QUrl, QSysInfo from PyQt5 import QtPrintSupport import sys, os, webbrowser tab = "\t" eof = "\n" tableheader2 = "<table><tr><th> Header 1 </th><th> Header 2 </th> \ </tr><tr><td> Line 1 </td><td> Line 1 </td></tr></table>" tableheader3 = "<table><tr><th> Header 1 </th><th> Header 2 </th><th> Header 3 </th> \ </tr><tr><td> Line 1 </td><td> Line 1 </td><td> Line 1 </td></tr></table>" class myEditor(QMainWindow): def __init__(self, parent = None): super(myEditor, self).__init__(parent) self.MaxRecentFiles = 5 self.windowList = [] self.recentFileActs = [] self.mainText = " " self.settings = QSettings('Axel Schneider', 'RichTextEdit') self.setAttribute(Qt.WA_DeleteOnClose) # Editor Widget ... # QIcon.setThemeName('gnome') self.editor = QTextEdit() self.editor.setStyleSheet(myStyleSheet(self)) self.editor.setTabStopWidth(14) self.editor.setContextMenuPolicy(Qt.CustomContextMenu) self.editor.customContextMenuRequested.connect(self.contextMenuRequested) self.createActions() self.createTollbarActions() self.createToolbar() self.createMenubar() def createTollbarActions(self): self.newAct = QAction("&New", self, shortcut=QKeySequence.New,statusTip="create a new file", triggered=self.newFile) self.newAct.setIcon(QIcon.fromTheme("gtk-new")) self.openAct = QAction("&Open", self, shortcut=QKeySequence.Open,statusTip="open file", triggered=self.openFile) self.openAct.setIcon(QIcon.fromTheme("gtk-open")) self.importRTFAct = QAction(QIcon.fromTheme("gnome-mime-application-rtf"), "import RTF", self, statusTip="import RTF File", triggered=self.importRTF) self.saveAct = QAction("&Save", self, shortcut=QKeySequence.Save,statusTip="save file", triggered=self.fileSave) self.saveAct.setIcon(QIcon.fromTheme("gtk-save")) self.saveAsAct = QAction("&Save as ...", self, shortcut=QKeySequence.SaveAs,statusTip="save file as ...", triggered=self.fileSaveAs) self.saveAsAct.setIcon(QIcon.fromTheme("gtk-save-as")) self.saveAsODFAct = QAction("&Save as OpenOffice Document", self, shortcut="Ctrl+Shift+e",statusTip="save file as OpenOffice Document", triggered=self.fileSaveAsODF) self.saveAsODFAct.setIcon(QIcon.fromTheme("libreoffice-writer")) self.pdfAct = QAction("export PDF", self, statusTip="save file as PDF", triggered=self.exportPDF) self.pdfAct.setIcon(QIcon.fromTheme("application-pdf")) ### print preview self.printPreviewAct = QAction("preview", self, shortcut=QKeySequence.Print,statusTip="Preview Document", triggered=self.handlePrintPreview) self.printPreviewAct.setIcon(QIcon.fromTheme("gtk-print-preview")) ### print self.printAct = QAction("print", self, shortcut=QKeySequence.Print,statusTip="Print Document", triggered=self.handlePrint) self.printAct.setIcon(QIcon.fromTheme("gtk-print")) ### show in Browser self.browserAct = QAction("preview in Browser", self, shortcut="F5",statusTip="preview in Browser", triggered=self.handleBrowser) self.browserAct.setIcon(QIcon.fromTheme("browser")) self.exitAct = QAction("Exit", self, shortcut=QKeySequence.Quit,statusTip="Exit", triggered=self.handleQuit) self.exitAct.setIcon(QIcon.fromTheme("application-exit")) self.repAllAct = QPushButton("replace all") self.repAllAct.setIcon(QIcon.fromTheme("gtk-find-and-replace")) self.repAllAct.setStatusTip("replace all") self.repAllAct.clicked.connect(self.replaceAll) self.bgAct = QAction(QIcon.fromTheme("preferences-color-symbolic"),"change Background Color", \ statusTip="change Background Color", triggered=self.changeBGColor) def createToolbar(self): ### begin toolbar self.file_tb = QToolBar(self) self.file_tb.setWindowTitle("File Toolbar") self.file_tb.addAction(self.newAct) self.file_tb.addAction(self.openAct) self.file_tb.addSeparator() self.file_tb.addAction(self.saveAct) self.file_tb.addAction(self.saveAsAct) self.file_tb.addSeparator() self.file_tb.addAction(self.saveAsODFAct) self.file_tb.addSeparator() self.file_tb.addAction(self.pdfAct) self.file_tb.addSeparator() self.file_tb.addAction(self.printPreviewAct) self.file_tb.addAction(self.printAct) self.file_tb.addSeparator() self.file_tb.addAction(self.browserAct) self.file_tb.addSeparator() self.file_tb.addAction(QAction(QIcon.fromTheme('image'), "insert Image", self, statusTip="insert an image", triggered = self.insertImage)) ### find / replace toolbar self.edit_tb = QToolBar(self) self.edit_tb.setWindowTitle("Find Toolbar") self.findfield = QLineEdit() self.findfield.addAction(QIcon.fromTheme("gtk-find"), 0) self.findfield.setClearButtonEnabled(True) self.findfield.setFixedWidth(200) self.findfield.setPlaceholderText("find") self.findfield.setStatusTip("press RETURN to find") self.findfield.setText("") self.findfield.returnPressed.connect(self.findText) self.edit_tb.addWidget(self.findfield) self.replacefield = QLineEdit() self.replacefield.addAction(QIcon.fromTheme("gtk-find-and-replace"), 0) self.replacefield.setClearButtonEnabled(True) self.replacefield.setFixedWidth(200) self.replacefield.setPlaceholderText("replace with") self.replacefield.setStatusTip("press RETURN to replace the first") self.replacefield.returnPressed.connect(self.replaceOne) self.edit_tb.addSeparator() self.edit_tb.addWidget(self.replacefield) self.edit_tb.addSeparator() self.edit_tb.addWidget(self.repAllAct) self.edit_tb.addSeparator() self.edit_tb.addAction(self.bgAct) ### Format Toolbar self.format_tb = QToolBar(self) self.format_tb.setWindowTitle("Format Toolbar") self.actionTextBold = QAction(QIcon.fromTheme('format-text-bold-symbolic'), "&Bold", self, priority=QAction.LowPriority, shortcut=Qt.CTRL + Qt.Key_B, triggered=self.textBold, checkable=True) self.actionTextBold.setStatusTip("bold") bold = QFont() bold.setBold(True) self.actionTextBold.setFont(bold) self.format_tb.addAction(self.actionTextBold) self.actionTextItalic = QAction(QIcon.fromTheme('format-text-italic-symbolic'), "&Italic", self, priority=QAction.LowPriority, shortcut=Qt.CTRL + Qt.Key_I, triggered=self.textItalic, checkable=True) italic = QFont() italic.setItalic(True) self.actionTextItalic.setFont(italic) self.format_tb.addAction(self.actionTextItalic) self.actionTextUnderline = QAction(QIcon.fromTheme('format-text-underline-symbolic'), "&Underline", self, priority=QAction.LowPriority, shortcut=Qt.CTRL + Qt.Key_U, triggered=self.textUnderline, checkable=True) underline = QFont() underline.setUnderline(True) self.actionTextUnderline.setFont(underline) self.format_tb.addAction(self.actionTextUnderline) self.format_tb.addSeparator() self.grp = QActionGroup(self, triggered=self.textAlign) if QApplication.isLeftToRight(): self.actionAlignLeft = QAction(QIcon.fromTheme('format-justify-left-symbolic'),"&Left", self.grp) self.actionAlignCenter = QAction(QIcon.fromTheme('format-justify-center-symbolic'),"C&enter", self.grp) self.actionAlignRight = QAction(QIcon.fromTheme('format-justify-right-symbolic'),"&Right", self.grp) else: self.actionAlignRight = QAction(QIcon.fromTheme('gtk-justify-right-symbolic'),"&Right", self.grp) self.actionAlignCenter = QAction(QIcon.fromTheme('gtk-justify-center-symbolic'),"C&enter", self.grp) self.actionAlignLeft = QAction(QIcon.fromTheme('format-justify-left-symbolic'),"&Left", self.grp) self.actionAlignJustify = QAction(QIcon.fromTheme('format-justify-fill-symbolic'),"&Justify", self.grp) self.actionAlignLeft.setShortcut(Qt.CTRL + Qt.Key_L) self.actionAlignLeft.setCheckable(True) self.actionAlignLeft.setPriority(QAction.LowPriority) self.actionAlignCenter.setShortcut(Qt.CTRL + Qt.Key_E) self.actionAlignCenter.setCheckable(True) self.actionAlignCenter.setPriority(QAction.LowPriority) self.actionAlignRight.setShortcut(Qt.CTRL + Qt.Key_R) self.actionAlignRight.setCheckable(True) self.actionAlignRight.setPriority(QAction.LowPriority) self.actionAlignJustify.setShortcut(Qt.CTRL + Qt.Key_J) self.actionAlignJustify.setCheckable(True) self.actionAlignJustify.setPriority(QAction.LowPriority) self.format_tb.addActions(self.grp.actions()) #self.indentAct = QAction(QIcon.fromTheme("format-indent-more-symbolic"), "indent more", self, triggered = self.indentLine, shortcut = "F8") # self.indentLessAct = QAction(QIcon.fromTheme("format-indent-less-symbolic"), "indent less", self, triggered = self.indentLessLine, shortcut = "F9") # self.format_tb.addAction(self.indentAct) # self.format_tb.addAction(self.indentLessAct) pix = QPixmap(16, 16) pix.fill(Qt.black) self.actionTextColor = QAction(QIcon(pix), "TextColor...", self, triggered=self.textColor) self.format_tb.addSeparator() self.format_tb.addAction(self.actionTextColor) self.font_tb = QToolBar(self) self.font_tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea) self.font_tb.setWindowTitle("Font Toolbar") self.comboStyle = QComboBox(self.font_tb) self.font_tb.addWidget(self.comboStyle) self.comboStyle.addItem("Standard") self.comboStyle.addItem("Bullet List (Disc)") self.comboStyle.addItem("Bullet List (Circle)") self.comboStyle.addItem("Bullet List (Square)") self.comboStyle.addItem("Ordered List (Decimal)") self.comboStyle.addItem("Ordered List (Alpha lower)") self.comboStyle.addItem("Ordered List (Alpha upper)") self.comboStyle.addItem("Ordered List (Roman lower)") self.comboStyle.addItem("Ordered List (Roman upper)") self.comboStyle.activated.connect(self.textStyle) self.comboFont = QFontComboBox(self.font_tb) self.font_tb.addSeparator() self.font_tb.addWidget(self.comboFont) self.comboFont.activated[str].connect(self.textFamily) self.comboSize = QComboBox(self.font_tb) self.font_tb.addSeparator() self.comboSize.setObjectName("comboSize") self.font_tb.addWidget(self.comboSize) self.comboSize.setEditable(True) db = QFontDatabase() for size in db.standardSizes(): self.comboSize.addItem("%s" % (size)) self.comboSize.addItem("%s" % (90)) self.comboSize.addItem("%s" % (100)) self.comboSize.addItem("%s" % (160)) self.comboSize.activated[str].connect(self.textSize) self.comboSize.setCurrentIndex( self.comboSize.findText( "%s" % (QApplication.font().pointSize()))) self.addToolBar(self.file_tb) self.addToolBar(self.format_tb) # self.addToolBarBreak(Qt.TopToolBarArea) self.addToolBar(self.font_tb) def importRTF(self): path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Documents/", "RTF Files (*.rtf)") if path: inFile = QFile(path) if path.endswith(".rtf"): os.system("cd /tmp;libreoffice --headless --convert-to html '" + path + "'") newfile = "/tmp/" + self.strippedName(path).replace(".rtf", ".html") self.openFileOnStart(newfile) self.statusBar().showMessage("File is in '/tmp' *** please use 'save as ...'") def msgbox(self,title, message): QMessageBox.warning(self, title, message) def indentLine(self): if not self.editor.textCursor().selectedText() == "": ot = self.editor.textCursor().selection().toHtml() self.msgbox("HTML", str(ot)) # self.editor.textCursor().insertText(tab) # self.editor.textCursor().insertHtml(QTextDocumentFragment.toHtml(ot)) # self.setModified(True) def indentLessLine(self): if not self.editor.textCursor().selectedText() == "": newline = u"\u2029" list = [] ot = self.editor.textCursor().selectedText() theList = ot.splitlines() linecount = ot.count(newline) for i in range(linecount + 1): list.insert(i, (theList[i]).replace(tab, "", 1)) self.editor.textCursor().insertText(newline.join(list)) self.setModified(True) def createMenubar(self): bar=self.menuBar() self.filemenu=bar.addMenu("File") self.filemenu.addAction(QIcon.fromTheme("dialog-question"),"about PyEdit", self.about, shortcut = "Ctrl+i") self.separatorAct = self.filemenu.addSeparator() self.filemenu.addAction(self.newAct) self.filemenu.addAction(self.openAct) self.filemenu.addAction(self.importRTFAct) self.filemenu.addAction(self.saveAct) self.filemenu.addAction(self.saveAsAct) self.filemenu.addSeparator() self.filemenu.addAction(self.saveAsODFAct) self.filemenu.addSeparator() self.filemenu.addAction(QIcon.fromTheme("application-pdf"),"export PDF", self.exportPDF) self.filemenu.addSeparator() for i in range(self.MaxRecentFiles): self.filemenu.addAction(self.recentFileActs[i]) self.updateRecentFileActions() self.filemenu.addSeparator() self.clearRecentAct = QAction("clear Recent Files List", self, triggered=self.clearRecentFiles) self.clearRecentAct.setIcon(QIcon.fromTheme("edit-clear")) self.filemenu.addAction(self.clearRecentAct) self.filemenu.addSeparator() self.filemenu.addAction(QAction(QIcon.fromTheme('html'), "get HTML (Document)", self, triggered = self.getHTML)) self.filemenu.addSeparator() self.filemenu.addAction(self.exitAct) # bar.setStyleSheet(myStyleSheet(self)) editmenu = bar.addMenu("Edit") editmenu.addAction(QAction(QIcon.fromTheme('edit-undo'), "Undo", self, triggered = self.editor.undo, shortcut = "Ctrl+u")) editmenu.addAction(QAction(QIcon.fromTheme('edit-redo'), "Redo", self, triggered = self.editor.redo, shortcut = "Shift+Ctrl+u")) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('edit-copy'), "Copy", self, triggered = self.editor.copy, shortcut = QKeySequence.Copy)) editmenu.addAction(QAction(QIcon.fromTheme('edit-cut'), "Cut", self, triggered = self.editor.cut, shortcut = QKeySequence.Cut)) editmenu.addAction(QAction(QIcon.fromTheme('edit-paste'), "Paste", self, triggered = self.editor.paste, shortcut = QKeySequence.Paste)) editmenu.addAction(QAction(QIcon.fromTheme('edit-delete'), "Delete", self, triggered = self.editor.cut, shortcut = QKeySequence.Delete)) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('edit-select-all'), "Select All", self, triggered = self.editor.selectAll, shortcut = QKeySequence.SelectAll)) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('edit-copy'), "grab selected line", self, triggered = self.grabLine)) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('image'), "insert Image", self, triggered = self.insertImage)) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('input-tablet'), "insert Table (2 Column)", self, triggered = self.insertTable)) editmenu.addAction(QAction(QIcon.fromTheme('input-tablet'), "insert Table (3 Column)", self, triggered = self.insertTable3)) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('text-html'), "convert from HTML", self, triggered = self.convertfromHTML, shortcut ="F10")) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('browser'), "insert Link", self, triggered = self.insertLink)) editmenu.addAction(QAction(QIcon.fromTheme('browser'), "edit Link", self, triggered = self.editLink)) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('input-tablet'), "edit body style", self, triggered = self.editBody)) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('input-tablet'), "edit HTML (selected Text)", self, triggered = self.editHTML)) editmenu.addSeparator() editmenu.addAction(QAction(QIcon.fromTheme('stock_calendar'), "insert Date", self, triggered = self.insertDate)) editmenu.addAction(QAction(QIcon.fromTheme('stock_calendar'), "insert Time", self, triggered = self.insertTime)) editmenu.addAction(QAction(QIcon.fromTheme('stock_calendar'), "insert Date && Time", self, triggered = self.insertDateTime)) editmenu.addSeparator() editmenu.addAction(self.bgAct) self.formatMenu = QMenu("F&ormat", self) self.formatMenu.addAction(self.actionTextBold) self.formatMenu.addAction(self.actionTextItalic) self.formatMenu.addAction(self.actionTextUnderline) self.formatMenu.addSeparator() self.formatMenu.addActions(self.grp.actions()) self.formatMenu.addSeparator() self.formatMenu.addAction(self.actionTextColor) bar.addMenu(self.formatMenu) # Laying out... layoutV = QVBoxLayout() layoutV.addWidget(self.edit_tb) layoutV.addWidget(self.editor) ### main window mq = QWidget(self) mq.setLayout(layoutV) self.setCentralWidget(mq) self.statusBar().showMessage("Welcome to RichTextEdit * " \ + QSysInfo.prettyProductName() + " * " + \ QSysInfo.kernelVersion() + " * " + QSysInfo.machineHostName() + " * " + \ QSysInfo.currentCpuArchitecture() + " * " + QSysInfo.buildAbi()) # Event Filter ... self.installEventFilter(self) self.cursor = QTextCursor() self.editor.setTextCursor(self.cursor) self.editor.setPlainText(self.mainText) self.editor.moveCursor(self.cursor.End) self.editor.textCursor().deletePreviousChar() self.editor.document().modificationChanged.connect(self.setWindowModified) self.extra_selections = [] self.fname = "" self.filename = "" self.editor.setFocus() self.setModified(False) self.fontChanged(self.editor.font()) self.colorChanged(self.editor.textColor()) self.alignmentChanged(self.editor.alignment()) self.editor.document().modificationChanged.connect( self.setWindowModified) self.setWindowModified(self.editor.document().isModified()) self.editor.setAcceptRichText(True) self.editor.currentCharFormatChanged.connect( self.currentCharFormatChanged) self.editor.cursorPositionChanged.connect(self.cursorPositionChanged) # QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged) def insertDate(self): import time from datetime import date today = date.today().strftime("%A, %d.%B %Y") self.editor.textCursor().insertText(today) def insertTime(self): import time from datetime import date today = time.strftime("%H:%M Uhr") self.editor.textCursor().insertText(today) def insertDateTime(self): self.insertDate() self.editor.textCursor().insertText(eof) self.insertTime() self.editor.textCursor().insertText(eof) def changeBGColor(self): all = self.editor.document().toHtml() bgcolor = all.partition("<body style=")[2].partition(">")[0].partition('bgcolor="')[2].partition('"')[0] if not bgcolor == "": col = QColorDialog.getColor(QColor(bgcolor), self) if not col.isValid(): return else: colorname = col.name() new = all.replace("bgcolor=" + '"' + bgcolor + '"', "bgcolor=" + '"' + colorname + '"') self.editor.document().setHtml(new) else: col = QColorDialog.getColor(QColor("#FFFFFF"), self) if not col.isValid(): return else: all = self.editor.document().toHtml() body = all.partition("<body style=")[2].partition(">")[0] newbody = body + "bgcolor=" + '"' + col.name() + '"' new = all.replace(body, newbody) self.editor.document().setHtml(new) def getHTML(self): all = self.editor.document().toHtml() clipboard = QApplication.clipboard() clipboard.setText(all) self.statusBar().showMessage("HTML is in clipboard") def editHTML(self): all = self.editor.textCursor().selection().toHtml() dlg = QInputDialog(self, Qt.Window) dlg.setGeometry(50, 50, 700, 400) dlg.setOption(QInputDialog.UsePlainTextEditForTextInput) new, ok = dlg.getMultiLineText(self, 'change HTML', "edit HTML", all) if ok: self.editor.textCursor().insertHtml(new) self.statusBar().showMessage("HTML changed") else: self.statusBar().showMessage("HTML not changed") def editBody(self): all = self.editor.document().toHtml() body = all.partition("<body style=")[2].partition(">")[0] dlg = QInputDialog() mybody, ok = dlg.getText(self, 'change body style', "", QLineEdit.Normal, body, Qt.Dialog) if ok: new = all.replace(body, mybody) self.editor.document().setHtml(new) self.statusBar().showMessage("body style changed") else: self.statusBar().showMessage("body style not changed") def insertTable(self): self.editor.append(tableheader2) def insertTable3(self): self.editor.append(tableheader3) def handleBrowser(self): if self.editor.toPlainText() == "": self.statusBar().showMessage("no text") else: if not self.editor.document().isModified() == True: webbrowser.open(self.filename, new=0, autoraise=True) else: myfilename = "/tmp/browser.html" writer = QTextDocumentWriter(myfilename) success = writer.write(self.editor.document()) if success: webbrowser.open(myfilename, new=0, autoraise=True) return success def contextMenuRequested(self,point): cmenu = QMenu() cmenu = self.editor.createStandardContextMenu() cmenu.addSeparator() cmenu.addAction(QAction(QIcon.fromTheme('edit-copy'), "grab this line", self, triggered = self.grabLine)) cmenu.addSeparator() cmenu.addAction(QAction(QIcon.fromTheme('image-x-generic'), "insert Image", self, triggered = self.insertImage)) cmenu.addSeparator() cmenu.addAction(QAction(QIcon.fromTheme('input-tablet'), "insert Table (2 Column)", self, triggered = self.insertTable)) cmenu.addAction(QAction(QIcon.fromTheme('input-tablet'), "insert Table (3 Column)", self, triggered = self.insertTable3))# cmenu.addSeparator() cmenu.addAction(QAction(QIcon.fromTheme('text-html'), "convert from HTML", self, triggered = self.convertfromHTML)) cmenu.addSeparator() cmenu.addAction(QAction(QIcon.fromTheme('text-plain'), "convert to Text", self, triggered = self.convertToHTML)) cmenu.addSeparator() cmenu.addAction(QAction(QIcon.fromTheme('browser'), "insert Link", self, triggered = self.insertLink)) cmenu.addAction(QAction(QIcon.fromTheme('browser'), "edit Link", self, triggered = self.editLink)) cmenu.addSeparator() cmenu.addAction(QAction(QIcon.fromTheme('input-tablet'), "edit HTML (selected Text)", self, triggered = self.editHTML)) cmenu.addSeparator() cmenu.addAction(self.bgAct) cmenu.exec_(self.editor.mapToGlobal(point)) def editLink(self): if not self.editor.textCursor().selectedText() == "": mt = self.editor.textCursor().selectedText() mytext = QTextDocumentFragment.toHtml(self.editor.textCursor().selection()) myurl = mytext.partition('<a href="')[2].partition('">')[0] dlg = QInputDialog() dlg.setOkButtonText("Change") mylink, ok = dlg.getText(self, 'change URL', "", QLineEdit.Normal, str(myurl), Qt.Dialog) if ok: if mylink.startswith("http"): self.editor.textCursor().insertHtml("<a href='" + mylink + "' target='_blank'>" + mt + "</a>") self.statusBar().showMessage("link added") else: self.statusBar().showMessage("this is no link") else: self.statusBar().showMessage("not changed") else: self.statusBar().showMessage("no text selected") def insertLink(self): if not self.editor.textCursor().selectedText() == "": mytext = self.editor.textCursor().selectedText() dlg = QInputDialog() mylink, ok = dlg.getText(self, 'insert URL', "", QLineEdit.Normal, "", Qt.Dialog) if ok: if str(mylink).startswith("http"): self.editor.textCursor().insertHtml("<a href='" + mylink + "' target='_blank'>" + mytext + "</a>") self.statusBar().showMessage("link added") else: self.statusBar().showMessage("this is no link") else: self.statusBar().showMessage("no link added") else: self.statusBar().showMessage("no text selected") def convertfromHTML(self): oldtext = self.editor.textCursor().selectedText() self.editor.textCursor().insertHtml(oldtext) self.statusBar().showMessage("converted to html") def convertToHTML(self): oldtext = QTextDocumentFragment.fromHtml(self.editor.textCursor().selectedText()) self.editor.textCursor().insertText(oldtext.toPlainText()) self.statusBar().showMessage("converted to plain text") def insertImage(self): path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Pictures/", "Images (*.png *.PNG *.jpg *.JPG *.bmp *.BMP *.xpm *.gif *.eps)") if path: #self.editor.append("<img src='" + path + "'/>") self.editor.textCursor().insertImage("file://" + path) self.statusBar().showMessage("'" + path + "' inserted") else: self.statusBar().showMessage("no image") def grabLine(self): text = self.editor.textCursor().block().text() clipboard = QApplication.clipboard() clipboard.setText(text) def about(self): link = "<p><a title='Axel Schneider' href='http://goodoldsongs.jimdo.com' target='_blank'>Axel Schneider</a></p>" title = "about RichTextEdit" message = "<span style='color: #1F9CDD; font-size: 24pt;font-weight: bold;'\ >RichTextEdit 1.0</strong></span></p><br>created by<h3>" + link + "</h3> with PyQt5<br>" \ + "<br>Copyright © 2017 The Qt Company Ltd and other contributors." \ + "<br>Qt and the Qt logo are trademarks of The Qt Company Ltd." msg = QMessageBox(QMessageBox.Information, title, message, QMessageBox.NoButton, self, Qt.Dialog|Qt.NoDropShadowWindowHint).show() def createActions(self): for i in range(self.MaxRecentFiles): self.recentFileActs.append( QAction(self, visible=False, triggered=self.openRecentFile)) def openRecentFile(self): action = self.sender() if action: if (self.maybeSave()): self.openFileOnStart(action.data()) ### New File def newFile(self): if self.maybeSave(): self.editor.clear() self.editor.setPlainText(self.mainText) self.filename = "" self.editor.moveCursor(self.cursor.End) self.editor.textCursor().deletePreviousChar() self.setWindowTitle("New[*]") self.setModified(False) ### open File def openFileOnStart(self, path=None): if path: inFile = QFile(path) if inFile.open(QFile.ReadWrite | QFile.Text): data = inFile.readAll() codec = QTextCodec.codecForHtml(data) unistr = codec.toUnicode(data) if Qt.mightBeRichText(unistr): self.editor.setHtml(unistr) else: self.editor.setPlainText(unistr) self.filename = path self.setModified(False) self.fname = QFileInfo(path).fileName() self.document = self.editor.document() self.setCurrentFile(path) self.statusBar().showMessage("loaded file '" + path + "'") ### open File def openFile(self, path=None): if self.maybeSave(): if not path: path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Documents/", "RichText Files (*.htm *.html);; Text Files (*.txt *.csv *.py);;All Files (*.*)") if path: inFile = QFile(path) self.openFileOnStart(path) def exportPDF(self): if self.editor.toPlainText() == "": self.statusBar().showMessage("no text") else: newname = self.strippedName(self.filename).replace(".html", ".pdf") fn, _ = QFileDialog.getSaveFileName(self, "PDF files (*.pdf);;All Files (*)", (QDir.homePath() + "/PDF/" + newname)) printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.HighResolution) printer.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat) printer.setOutputFileName(fn) self.editor.document().print_(printer) ### save def fileSave(self): if not self.filename: return self.fileSaveAs() if self.isModified(): writer = QTextDocumentWriter(self.filename) success = writer.write(self.editor.document()) if success: self.editor.document().setModified(False) self.setCurrentFile(self.filename) self.statusBar().showMessage("saved file '" + self.filename + "'") return success else: self.statusBar().showMessage("already saved") def fileSaveODF(self, fn): writer = QTextDocumentWriter(fn) # writer.setFormat("ODF") success = writer.write(self.editor.document()) if success: self.statusBar().showMessage("saved file '" + fn + "'") return success def fileSaveAs(self): if self.editor.toPlainText() == "": self.statusBar().showMessage("no text") else: fn, _ = QFileDialog.getSaveFileName(self, "Save as...", self.filename, "HTML-Files (*.html *.htm)") if not fn: return False lfn = fn.lower() if not lfn.endswith(('.htm', '.html')): fn += '.html' self.filename = fn return self.fileSave() def fileSaveAsODF(self): if self.editor.toPlainText() == "": self.statusBar().showMessage("no text") else: fn, _ = QFileDialog.getSaveFileName(self, "Save as...", self.strippedName(self.filename).replace(".html",""), "OpenOffice-Files (*.odt)") if not fn: return False lfn = fn.lower() if not lfn.endswith(('.odt')): fn += '.odt' return self.fileSaveODF(fn) def closeEvent(self, e): if self.maybeSave(): e.accept() else: e.ignore() ### ask to save def maybeSave(self): if not self.isModified(): return True if self.filename.startswith(':/'): return True ret = QMessageBox.question(self, "Message", "<h4><p>The document was modified.</p>\n" \ "<p>Do you want to save changes?</p></h4>", QMessageBox.Yes | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Yes: if self.filename == "": self.fileSaveAs() return False else: self.fileSave() return True if ret == QMessageBox.Cancel: return False return True def findText(self): word = self.findfield.text() if self.editor.find(word): return else: self.editor.moveCursor(QTextCursor.Start) if self.editor.find(word): return def handleQuit(self): print("Goodbye ...") app.quit() def document(self): return self.editor.document def isModified(self): return self.editor.document().isModified() def setModified(self, modified): self.editor.document().setModified(modified) def setLineWrapMode(self, mode): self.editor.setLineWrapMode(mode) def clear(self): self.editor.clear() def setPlainText(self, *args, **kwargs): self.editor.setPlainText(*args, **kwargs) def setDocumentTitle(self, *args, **kwargs): self.editor.setDocumentTitle(*args, **kwargs) def set_number_bar_visible(self, value): self.numbers.setVisible(value) def replaceAll(self): oldtext = self.findfield.text() newtext = self.replacefield.text() if not oldtext == "": h = self.editor.toHtml().replace(oldtext, newtext) self.editor.setText(h) self.setModified(True) self.statusBar().showMessage("all replaced") else: self.statusBar().showMessage("nothing to replace") def replaceOne(self): oldtext = self.findfield.text() newtext = self.replacefield.text() if not oldtext == "": h = self.editor.toHtml().replace(oldtext, newtext, 1) self.editor.setText(h) self.setModified(True) self.statusBar().showMessage("one replaced") else: self.statusBar().showMessage("nothing to replace") def setCurrentFile(self, fileName): self.filename = fileName if self.filename: self.setWindowTitle(self.strippedName(self.filename) + "[*]") else: self.setWindowTitle("no File") files = self.settings.value('recentFileList', []) try: files.remove(fileName) except ValueError: pass files.insert(0, fileName) del files[self.MaxRecentFiles:] self.settings.setValue('recentFileList', files) for widget in QApplication.topLevelWidgets(): if isinstance(widget, myEditor): widget.updateRecentFileActions() def updateRecentFileActions(self): mytext = "" files = self.settings.value('recentFileList', []) numRecentFiles = min(len(files), self.MaxRecentFiles) for i in range(numRecentFiles): text = "&%d %s" % (i + 1, self.strippedName(files[i])) self.recentFileActs[i].setText(text) self.recentFileActs[i].setData(files[i]) self.recentFileActs[i].setVisible(True) self.recentFileActs[i].setIcon(QIcon.fromTheme("gnome-mime-text-x")) for j in range(numRecentFiles, self.MaxRecentFiles): self.recentFileActs[j].setVisible(False) self.separatorAct.setVisible((numRecentFiles > 0)) def clearRecentFiles(self, fileName): self.settings.clear() self.updateRecentFileActions() def strippedName(self, fullFileName): return QFileInfo(fullFileName).fileName() def textBold(self): fmt = QTextCharFormat() fmt.setFontWeight(self.actionTextBold.isChecked() and QFont.Bold or QFont.Normal) self.mergeFormatOnWordOrSelection(fmt) def textUnderline(self): fmt = QTextCharFormat() fmt.setFontUnderline(self.actionTextUnderline.isChecked()) self.mergeFormatOnWordOrSelection(fmt) def textItalic(self): fmt = QTextCharFormat() fmt.setFontItalic(self.actionTextItalic.isChecked()) self.mergeFormatOnWordOrSelection(fmt) def textFamily(self, family): fmt = QTextCharFormat() fmt.setFontFamily(family) self.mergeFormatOnWordOrSelection(fmt) def textSize(self, pointSize): pointSize = float(self.comboSize.currentText()) if pointSize > 0: fmt = QTextCharFormat() fmt.setFontPointSize(pointSize) self.mergeFormatOnWordOrSelection(fmt) def textStyle(self, styleIndex): cursor = self.editor.textCursor() if styleIndex: styleDict = { 1: QTextListFormat.ListDisc, 2: QTextListFormat.ListCircle, 3: QTextListFormat.ListSquare, 4: QTextListFormat.ListDecimal, 5: QTextListFormat.ListLowerAlpha, 6: QTextListFormat.ListUpperAlpha, 7: QTextListFormat.ListLowerRoman, 8: QTextListFormat.ListUpperRoman, } style = styleDict.get(styleIndex, QTextListFormat.ListDisc) cursor.beginEditBlock() blockFmt = cursor.blockFormat() listFmt = QTextListFormat() if cursor.currentList(): listFmt = cursor.currentList().format() else: listFmt.setIndent(1) blockFmt.setIndent(0) cursor.setBlockFormat(blockFmt) listFmt.setStyle(style) cursor.createList(listFmt) cursor.endEditBlock() else: bfmt = QTextBlockFormat() bfmt.setObjectIndex(-1) cursor.mergeBlockFormat(bfmt) def textColor(self): col = QColorDialog.getColor(self.editor.textColor(), self) if not col.isValid(): return fmt = QTextCharFormat() fmt.setForeground(col) self.mergeFormatOnWordOrSelection(fmt) self.colorChanged(col) def textAlign(self, action): if action == self.actionAlignLeft: self.editor.setAlignment(Qt.AlignLeft | Qt.AlignAbsolute) elif action == self.actionAlignCenter: self.editor.setAlignment(Qt.AlignHCenter) elif action == self.actionAlignRight: self.editor.setAlignment(Qt.AlignRight | Qt.AlignAbsolute) elif action == self.actionAlignJustify: self.editor.setAlignment(Qt.AlignJustify) def currentCharFormatChanged(self, format): self.fontChanged(format.font()) self.colorChanged(format.foreground().color()) def cursorPositionChanged(self): self.alignmentChanged(self.editor.alignment()) def clipboardDataChanged(self): self.actionPaste.setEnabled(len(QApplication.clipboard().text()) != 0) def mergeFormatOnWordOrSelection(self, format): cursor = self.editor.textCursor() if not cursor.hasSelection(): cursor.select(QTextCursor.WordUnderCursor) cursor.mergeCharFormat(format) self.editor.mergeCurrentCharFormat(format) def fontChanged(self, font): self.comboFont.setCurrentIndex( self.comboFont.findText(QFontInfo(font).family())) self.comboSize.setCurrentIndex( self.comboSize.findText("%s" % font.pointSize())) self.actionTextBold.setChecked(font.bold()) self.actionTextItalic.setChecked(font.italic()) self.actionTextUnderline.setChecked(font.underline()) def colorChanged(self, color): pix = QPixmap(26, 20) pix.fill(color) self.actionTextColor.setIcon(QIcon(pix)) def alignmentChanged(self, alignment): if alignment & Qt.AlignLeft: self.actionAlignLeft.setChecked(True) elif alignment & Qt.AlignHCenter: self.actionAlignCenter.setChecked(True) elif alignment & Qt.AlignRight: self.actionAlignRight.setChecked(True) elif alignment & Qt.AlignJustify: self.actionAlignJustify.setChecked(True) def handlePrint(self): if self.editor.toPlainText() == "": self.statusBar().showMessage("no text") else: dialog = QtPrintSupport.QPrintDialog() if dialog.exec_() == QDialog.Accepted: self.handlePaintRequest(dialog.printer()) self.statusBar().showMessage("Document printed") def handlePrintPreview(self): if self.editor.toPlainText() == "": self.statusBar().showMessage("no text") else: dialog = QtPrintSupport.QPrintPreviewDialog() dialog.setGeometry(30, 0, self.width() - 60, self.height() - 60) dialog.paintRequested.connect(self.handlePaintRequest) dialog.exec_() self.statusBar().showMessage("Print Preview closed") def handlePaintRequest(self, printer): printer.setDocName(self.filename) document = self.editor.document() document.print_(printer) def myStyleSheet(self): return """ QTextEdit { background: #fafafa; color: #202020; border: 1px solid #1EAE3D; selection-background-color: #729fcf; selection-color: #ffffff; } """ if __name__ == '__main__': app = QApplication(sys.argv) win = myEditor() win.setWindowIcon(QIcon.fromTheme("gnome-mime-application-rtf")) win.setWindowTitle("RichTextEdit" + "[*]") win.setMinimumSize(640,250) win.showMaximized() if len(sys.argv) > 1: print(sys.argv[1]) win.openFileOnStart(sys.argv[1]) app.exec_()