RichTextEditor - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: General (https://python-forum.io/forum-1.html) +--- Forum: Code sharing (https://python-forum.io/forum-5.html) +--- Thread: RichTextEditor (/thread-7419.html) Pages:
1
2
|
RichTextEditor - Axel_Erfurt - Jan-09-2018 based on the PyQt5 'RichText' example. 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_() RE: RichTextEditor - Gribouillis - Jan-09-2018 Impressive effort, but I get Traceback (most recent call last): File "RichTextEdit.py", line 12, in <module> import mycalendar ImportError: No module named 'mycalendar' RE: RichTextEditor - Axel_Erfurt - Jan-09-2018 sorry , remove the line, it was for a test. I've updated the code in first post. RE: RichTextEditor - Gribouillis - Jan-09-2018 Traceback (most recent call last): File "RichTextEdit.py", line 979, in <module> win = myEditor() File "RichTextEdit.py", line 39, in __init__ self.createTollbarActions() File "RichTextEdit.py", line 73, in createTollbarActions statusTip="change Background Color", triggered=self.changeBGColor) TypeError: arguments did not match any overloaded call: QAction(QObject): argument 1 has unexpected type 'QIcon' QAction(str, QObject): argument 1 has unexpected type 'QIcon' QAction(QIcon, str, QObject): not enough argumentsI'm using the Github code with python 3.5.2. RE: RichTextEditor - Axel_Erfurt - Jan-09-2018 I get the same error on python 2, but not in python 3.5.2 on Linux RE: RichTextEditor - Gribouillis - Jan-09-2018 Well I'm in Kubuntu 16.04.3 with python 3.5.2. I have the package python3-pyqt5 installed. All the packages are up to date, and I get this error. You could perhaps test it in a virtual host. λ sudo dpkg -l | grep python3-pyqt5 ii python3-pyqt5 5.5.1+dfsg-3ubuntu4 amd64 Python 3 bindings for Qt5 RE: RichTextEditor - Axel_Erfurt - Jan-09-2018 I get his: [inline]sudo dpkg -l | grep python3-pyqt5 ii python3-pyqt5 5.5.1+dfsg-3ubuntu4 amd64 Python 3 bindings for Qt5 ii python3-pyqt5.qtmultimedia 5.5.1+dfsg-3ubuntu4 amd64 Python 3 bindings for Qt5's Multimedia module ii python3-pyqt5.qtwebkit 5.5.1+dfsg-3ubuntu4 amd64 Python 3 bindings for Qt5's WebKit module[/inline] are you sure using python 3? python3 /path/to/RichTextEdit.py
RE: RichTextEditor - Gribouillis - Jan-09-2018 I'm trying to show a screenshot: RE: RichTextEditor - Axel_Erfurt - Jan-09-2018 replace the line self.bgAct = .... with: self.bgAct = QAction("change Background Color",self, triggered=self.changeBGColor) self.bgAct.setStatusTip("change Background Color") self.bgAct.setIcon(QIcon.fromTheme("preferences-color-symbolic"))that makes it working on python 2 for me. RE: RichTextEditor - Gribouillis - Jan-09-2018 I finally got it working. I had to remove most of the welcome message because of attributes errors on QSysInfo around line 350. Traceback (most recent call last): File "RichTextEdit.py", line 982, in <module> win = myEditor() File "RichTextEdit.py", line 41, in __init__ self.createMenubar() File "RichTextEdit.py", line 352, in createMenubar QSysInfo.currentCpuArchitecture() + " * " + QSysInfo.buildAbi()) AttributeError: type object 'QSysInfo' has no attribute 'prettyProductName'What? you're indenting python code with TAB characters instead of 4 SPACES! That's a very bad habit because most people use four spaces. You can configure your editor to insert 4 spaces when you hit the TAB key. I had to reindent the code with the old Tim Peters' reindent script to replace the tabs and implement the changes you suggested (he is the guy who authored the Zen of python!). The editor window looks quite usable. I'm missing a shortcut to insert titles. Remark: you are using a lot of line continuation characters \ . You never need them in python. Using parentheses, one can easily write multiline statements such as from foo import (a, b, c, d e, f, g, h, i, j, k, l, ) |