examples/webenginewidgets/markdowneditor#
(You can also check this code in the repository)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtCore import QObject, Property, Signal
class Document(QObject):
textChanged = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
self._text = ''
def text(self):
return self._text
def setText(self, t):
if t != self._text:
self._text = t
self.textChanged.emit(t)
text = Property(str, text, setText, notify=textChanged)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
"""PySide6 Markdown Editor Example"""
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtWidgets import QApplication
from mainwindow import MainWindow
import rc_markdowneditor
if __name__ == '__main__':
app = QApplication(sys.argv)
QCoreApplication.setOrganizationName("QtExamples")
window = MainWindow()
window.show()
sys.exit(app.exec())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtCore import QDir, QFile, QIODevice, QUrl, Qt, Slot
from PySide6.QtGui import QFontDatabase
from PySide6.QtWebChannel import QWebChannel
from PySide6.QtWidgets import QDialog, QFileDialog, QMainWindow, QMessageBox
from ui_mainwindow import Ui_MainWindow
from document import Document
from previewpage import PreviewPage
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.m_file_path = ''
self.m_content = Document()
self._ui = Ui_MainWindow()
self._ui.setupUi(self)
font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
self._ui.editor.setFont(font)
self._ui.preview.setContextMenuPolicy(Qt.NoContextMenu)
self._page = PreviewPage(self)
self._ui.preview.setPage(self._page)
self._ui.editor.textChanged.connect(self.plainTextEditChanged)
self._channel = QWebChannel(self)
self._channel.registerObject("content", self.m_content)
self._page.setWebChannel(self._channel)
self._ui.preview.setUrl(QUrl("qrc:/index.html"))
self._ui.actionNew.triggered.connect(self.onFileNew)
self._ui.actionOpen.triggered.connect(self.onFileOpen)
self._ui.actionSave.triggered.connect(self.onFileSave)
self._ui.actionSaveAs.triggered.connect(self.onFileSaveAs)
self._ui.actionExit.triggered.connect(self.close)
self._ui.editor.document().modificationChanged.connect(self._ui.actionSave.setEnabled)
defaultTextFile = QFile(":/default.md")
defaultTextFile.open(QIODevice.ReadOnly)
data = defaultTextFile.readAll()
self._ui.editor.setPlainText(data.data().decode('utf8'))
@Slot()
def plainTextEditChanged(self):
self.m_content.setText(self._ui.editor.toPlainText())
@Slot(str)
def openFile(self, path):
f = QFile(path)
name = QDir.toNativeSeparators(path)
if not f.open(QIODevice.ReadOnly):
error = f.errorString()
QMessageBox.warning(self, self.windowTitle(),
f"Could not open file {name}: {error}")
return
self.m_file_path = path
data = f.readAll()
self._ui.editor.setPlainText(data.data().decode('utf8'))
self.statusBar().showMessage(f"Opened {name}")
def isModified(self):
return self._ui.editor.document().isModified()
@Slot()
def onFileNew(self):
if self.isModified():
m = "You have unsaved changes. Do you want to create a new document anyway?"
button = QMessageBox.question(self, self.windowTitle(), m)
if button != QMessageBox.Yes:
return
self.m_file_path = ''
self._ui.editor.setPlainText(tr("## New document"))
self._ui.editor.document().setModified(False)
@Slot()
def onFileOpen(self):
if self.isModified():
m = "You have unsaved changes. Do you want to open a new document anyway?"
button = QMessageBox.question(self, self.windowTitle(), m)
if button != QMessageBox.Yes:
return
dialog = QFileDialog(self)
dialog.setWindowTitle("Open MarkDown File")
dialog.setMimeTypeFilters(["text/markdown"])
dialog.setAcceptMode(QFileDialog.AcceptOpen)
if dialog.exec() == QDialog.Accepted:
self.openFile(dialog.selectedFiles()[0])
@Slot()
def onFileSave(self):
if not self.m_file_path:
self.onFileSaveAs()
if not self.m_file_path:
return
f = QFile(self.m_file_path)
name = QDir.toNativeSeparators(self.m_file_path)
if not f.open(QIODevice.WriteOnly | QIODevice.Text):
error = f.errorString()
QMessageBox.warning(self, windowTitle(),
f"Could not write to file {name}: {error}")
return
text = self._ui.editor.toPlainText()
f.write(bytes(text, encoding='utf8'))
f.close()
self.statusBar().showMessage(f"Wrote {name}")
@Slot()
def onFileSaveAs(self):
dialog = QFileDialog(self)
dialog.setWindowTitle("Open MarkDown File")
dialog.setMimeTypeFilters(["text/markdown"])
dialog.setAcceptMode(QFileDialog.AcceptSave)
dialog.setDefaultSuffix("md")
if dialog.exec() != QDialog.Accepted:
return
path = dialog.selectedFiles()[0]
self.m_file_path = path
self.onFileSave()
def closeEvent(self, event):
if self.isModified():
m = "You have unsaved changes. Do you want to exit anyway?"
button = QMessageBox.question(self, self.windowTitle(), m)
if button != QMessageBox.Yes:
event.ignore()
else:
event.accept()
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MarkDown Editor</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QPlainTextEdit" name="editor"/>
<widget class="QWebEngineView" name="preview" native="true"/>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
<property name="title">
<string>&File</string>
</property>
<addaction name="actionNew"/>
<addaction name="actionOpen"/>
<addaction name="actionSave"/>
<addaction name="actionSaveAs"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
<addaction name="menu_File"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionOpen">
<property name="text">
<string>&Open...</string>
</property>
<property name="toolTip">
<string>Open document</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
<action name="actionSave">
<property name="text">
<string>&Save</string>
</property>
<property name="toolTip">
<string>Save current document</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>E&xit</string>
</property>
<property name="toolTip">
<string>Exit editor</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="actionSaveAs">
<property name="text">
<string>Save &As...</string>
</property>
<property name="toolTip">
<string>Save document under different name</string>
</property>
</action>
<action name="actionNew">
<property name="text">
<string>&New</string>
</property>
<property name="toolTip">
<string>Create new document</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtGui import QDesktopServices
from PySide6.QtWebEngineCore import QWebEnginePage
class PreviewPage(QWebEnginePage):
def __init__(self, parent=None):
super().__init__(parent)
def acceptNavigationRequest(self, url, type, isMainFrame):
# Only allow qrc:/index.html.
if url.scheme() == "qrc":
return True
QDesktopServices.openUrl(url)
return False
<RCC>
<qresource prefix="/">
<file>default.md</file>
<file>index.html</file>
<file>3rdparty/markdown.css</file>
<file>3rdparty/marked.js</file>
</qresource>
</RCC>
# -*- coding: utf-8 -*-
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QCursor, QFont, QFontDatabase, QGradient,
QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWebEngineWidgets import QWebEngineView
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QMainWindow, QMenu,
QMenuBar, QPlainTextEdit, QSizePolicy, QSplitter,
QStatusBar, QWidget)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(800, 600)
self.actionOpen = QAction(MainWindow)
self.actionOpen.setObjectName(u"actionOpen")
self.actionSave = QAction(MainWindow)
self.actionSave.setObjectName(u"actionSave")
self.actionExit = QAction(MainWindow)
self.actionExit.setObjectName(u"actionExit")
self.actionSaveAs = QAction(MainWindow)
self.actionSaveAs.setObjectName(u"actionSaveAs")
self.actionNew = QAction(MainWindow)
self.actionNew.setObjectName(u"actionNew")
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.horizontalLayout = QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.splitter = QSplitter(self.centralwidget)
self.splitter.setObjectName(u"splitter")
self.splitter.setOrientation(Qt.Horizontal)
self.editor = QPlainTextEdit(self.splitter)
self.editor.setObjectName(u"editor")
self.splitter.addWidget(self.editor)
self.preview = QWebEngineView(self.splitter)
self.preview.setObjectName(u"preview")
self.splitter.addWidget(self.preview)
self.horizontalLayout.addWidget(self.splitter)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 800, 26))
self.menu_File = QMenu(self.menubar)
self.menu_File.setObjectName(u"menu_File")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menu_File.menuAction())
self.menu_File.addAction(self.actionNew)
self.menu_File.addAction(self.actionOpen)
self.menu_File.addAction(self.actionSave)
self.menu_File.addAction(self.actionSaveAs)
self.menu_File.addSeparator()
self.menu_File.addAction(self.actionExit)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MarkDown Editor", None))
self.actionOpen.setText(QCoreApplication.translate("MainWindow", u"&Open...", None))
#if QT_CONFIG(tooltip)
self.actionOpen.setToolTip(QCoreApplication.translate("MainWindow", u"Open document", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(shortcut)
self.actionOpen.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+O", None))
#endif // QT_CONFIG(shortcut)
self.actionSave.setText(QCoreApplication.translate("MainWindow", u"&Save", None))
#if QT_CONFIG(tooltip)
self.actionSave.setToolTip(QCoreApplication.translate("MainWindow", u"Save current document", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(shortcut)
self.actionSave.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+S", None))
#endif // QT_CONFIG(shortcut)
self.actionExit.setText(QCoreApplication.translate("MainWindow", u"E&xit", None))
#if QT_CONFIG(tooltip)
self.actionExit.setToolTip(QCoreApplication.translate("MainWindow", u"Exit editor", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(shortcut)
self.actionExit.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+Q", None))
#endif // QT_CONFIG(shortcut)
self.actionSaveAs.setText(QCoreApplication.translate("MainWindow", u"Save &As...", None))
#if QT_CONFIG(tooltip)
self.actionSaveAs.setToolTip(QCoreApplication.translate("MainWindow", u"Save document under different name", None))
#endif // QT_CONFIG(tooltip)
self.actionNew.setText(QCoreApplication.translate("MainWindow", u"&New", None))
#if QT_CONFIG(tooltip)
self.actionNew.setToolTip(QCoreApplication.translate("MainWindow", u"Create new document", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(shortcut)
self.actionNew.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+N", None))
#endif // QT_CONFIG(shortcut)
self.menu_File.setTitle(QCoreApplication.translate("MainWindow", u"&File", None))
# retranslateUi