Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
RichTextEditor
#1
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_()
Reply


Messages In This Thread
RichTextEditor - by Axel_Erfurt - Jan-09-2018, 06:56 PM
RE: RichTextEditor - by Gribouillis - Jan-09-2018, 07:18 PM
RE: RichTextEditor - by Axel_Erfurt - Jan-09-2018, 07:24 PM
RE: RichTextEditor - by Gribouillis - Jan-09-2018, 07:26 PM
RE: RichTextEditor - by Axel_Erfurt - Jan-09-2018, 07:35 PM
RE: RichTextEditor - by Gribouillis - Jan-09-2018, 08:31 PM
RE: RichTextEditor - by Axel_Erfurt - Jan-09-2018, 09:06 PM
RE: RichTextEditor - by Gribouillis - Jan-09-2018, 09:17 PM
RE: RichTextEditor - by Axel_Erfurt - Jan-09-2018, 09:50 PM
RE: RichTextEditor - by Gribouillis - Jan-09-2018, 11:00 PM
RE: RichTextEditor - by Axel_Erfurt - Jan-10-2018, 05:58 PM

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020