Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
RichTextEditor (PyQt5)
#!/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, QPlainTextEdit
from PyQt5.QtGui import QIcon, QPainter, QTextFormat, QColor, QTextCursor, QKeySequence, QClipboard, \
QTextCharFormat, QTextCharFormat, QFont, QPixmap, QFontDatabase, QFontInfo, QTextDocumentWriter, \
QImage, QTextListFormat, QTextBlockFormat, QTextDocumentFragment, QKeyEvent
from PyQt5.QtCore import Qt, QDir, QFile, QFileInfo, QTextStream, QSettings, QTextCodec, QSize, QMimeData, QUrl, QSysInfo, QEvent
from PyQt5 import QtPrintSupport
import sys, os, webbrowser
tab = "\t"
eof = "\n"
tableheader2 = "<table></tr><tr><td> Column1 </td><td> Column2 </td></tr></table>"
tableheader3 = "<table></tr><tr><td> Column1 </td><td> Column2 </td><td> Column3 </td></tr></table>"
class htmlEditor(QWidget):
def __init__(self, parent = None, text = ""):
super(htmlEditor, self).__init__(parent)
self.ed = QPlainTextEdit()
self.btnOK = QPushButton("OK", clicked=self.sendText)
self.btnCancel = QPushButton("Cancel", clicked=self.cancelAction)
self.hbox = QHBoxLayout()
self.hbox.addWidget(self.btnCancel)
self.hbox.addWidget(self.btnOK)
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.ed)
self.vbox.addLayout(self.hbox)
self.setLayout(self.vbox)
#self.ed.setPlainText(text)
def sendText(self):
return self.ed.toPlainText()
def cancelAction(self):
self.close()
class myEditor(QMainWindow):
def __init__(self, parent = None):
super(myEditor, self).__init__(parent)
# def keyPressEvent(self, event):
# if event.key() == Qt.Key_Tab:
# print ("tab pressed")
self.setStyleSheet(myStyleSheet(self))
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("change Background Color",self, triggered=self.changeBGColor)
self.bgAct.setStatusTip("change Background Color")
self.bgAct.setIcon(QIcon.fromTheme("preferences-color-symbolic"))
def createToolbar(self):
### begin toolbar
self.file_tb = QToolBar(self)
self.file_tb.setIconSize(QSize(16, 16))
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.setIconSize(QSize(16, 16))
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.setIconSize(QSize(16, 16))
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):
self.newFile()
rtext = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body>""" + '\n'
btext = """<!--EndFragment--></p></body></html>"""
path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Dokumente/",
"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")
with open(newfile, 'r') as f:
t = f.read().replace(rtext, "").replace(btext, "")
self.editor.insertHtml(t.replace(rtext, "").replace(btext, ""))
self.setModified(False)
all = self.editor.document().toHtml()
clipboard = QApplication.clipboard()
clipboard.setText(all)
#self.setModified(False)
self.newFile()
self.editor.insertHtml(clipboard.text())
self.statusBar().showMessage("File is in '/tmp' *** please use 'save as ...'")
#self.setModified(True)
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 * ")
# 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):
rtext = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body>""" + '\n'
btext = """<!--EndFragment--></p></body></html>"""
all = self.editor.textCursor().selection().toHtml()
#dlg = QInputDialog(self, Qt.Window)
#dlg.setOption(QInputDialog.UsePlainTextEditForTextInput, True)
#new, ok = dlg.getMultiLineText(self, 'change HTML', "edit HTML", all.replace(rtext, ""))
#if ok:
# self.editor.textCursor().insertHtml(new)
# self.statusBar().showMessage("HTML changed")
#else:
# self.statusBar().showMessage("HTML not changed")
self.heditor = htmlEditor()
self.heditor.ed.setPlainText(all.replace(rtext, "").replace(btext, ""))
self.heditor.setGeometry(0, 0, 800, 600)
self.heditor.show()
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.textCursor().insertHtml(tableheader2)
def insertTable3(self):
self.editor.textCursor().insertHtml(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() + "/Dokumente/",
"RichText Files (*.htm *.html *.xhtml);; 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")
### save
def fileSave2(self):
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 + "'")
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 fn:
if not fn.endswith(('.htm', '.html')):
fn += '.html'
self.filename = fn
return self.fileSave2()
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;
}
QMenuBar
{
background: transparent;
border: 0px;
}
QToolBar
{
background: transparent;
border: 0px;
}
QMainWindow
{
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #E1E1E1, stop: 0.4 #DDDDDD,
stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3);
}
"""
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_()
@tarekbardouni
Copy link

tarekbardouni commented Jul 5, 2022

Hi Alex,
I downloaded your python RichTextEditor and I experienced a problem with displaying icone images of the application.
Your feedback please.
Regards,
Tarek

@Axel-Erfurt
Copy link
Author

Axel-Erfurt commented Jul 6, 2022

What is your Operating System?

@tarekbardouni
Copy link

tarekbardouni commented Jul 6, 2022

Ubuntu 21.04 and python 3.9.5 under miniconda3 the icons are hiden.
But outside miniconda it is working correctely.
Regards,
Tarek

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment