| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /lib/calibre/calibre/gui2/ |
Upload File : |
#!/usr/bin/env python3
# License: GPLv3 Copyright: 2023, un_pogaz <un.pogaz@gmail.com>
import os
from qt.core import (
QDialog, QDialogButtonBox, QPlainTextEdit, QSize, Qt, QTabWidget, QUrl,
QVBoxLayout, QWidget, pyqtSignal,
)
from calibre.gui2 import safe_open_url, gprefs
from calibre.gui2.book_details import resolved_css
from calibre.gui2.widgets2 import HTMLDisplay
from calibre.library.comments import markdown as get_markdown
class Preview(HTMLDisplay):
def __init__(self, parent=None):
super().__init__(parent)
self.setDefaultStyleSheet(resolved_css())
self.setTabChangesFocus(True)
self.base_url = None
def loadResource(self, rtype, qurl):
if self.base_url is not None and qurl.isRelative():
qurl = self.base_url.resolved(qurl)
return super().loadResource(rtype, qurl)
class MarkdownEdit(QPlainTextEdit):
smarten_punctuation = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
from calibre.gui2.markdown_syntax_highlighter import MarkdownHighlighter
self.highlighter = MarkdownHighlighter(self.document())
def contextMenuEvent(self, ev):
m = self.createStandardContextMenu()
m.addSeparator()
m.addAction(_('Smarten punctuation'), self.smarten_punctuation.emit)
m.exec(ev.globalPos())
class MarkdownEditDialog(QDialog):
def __init__(self, parent, text, column_name=None, base_url=None):
QDialog.__init__(self, parent)
self.setObjectName("MarkdownEditDialog")
self.setWindowTitle(_("Edit Markdown"))
self.verticalLayout = l = QVBoxLayout(self)
self.textbox = editor = Editor(self)
editor.set_base_url(base_url)
self.buttonBox = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, self)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
l.addWidget(editor)
l.addWidget(bb)
# Remove help icon on title bar
icon = self.windowIcon()
self.setWindowFlags(self.windowFlags()&(~Qt.WindowType.WindowContextHelpButtonHint))
self.setWindowIcon(icon)
self.textbox.markdown =text
# self.textbox.wyswyg_dirtied()
if column_name:
self.setWindowTitle(_('Edit "{0}"').format(column_name))
self.restore_geometry(gprefs, 'markdown_edit_dialog_geom')
def sizeHint(self):
return QSize(650, 600)
def accept(self):
self.save_geometry(gprefs, 'markdown_edit_dialog_geom')
QDialog.accept(self)
def reject(self):
self.save_geometry(gprefs, 'markdown_edit_dialog_geom')
QDialog.reject(self)
def closeEvent(self, ev):
self.save_geometry(gprefs, 'markdown_edit_dialog_geom')
return QDialog.closeEvent(self, ev)
@property
def text(self):
return self.textbox.markdown
@text.setter
def text(self, val):
self.textbox.markdown = val or ''
class Editor(QWidget): # {{{
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.base_url = None
self._layout = QVBoxLayout(self)
self.setLayout(self._layout)
self.tabs = QTabWidget(self)
self.tabs.setTabPosition(QTabWidget.TabPosition.South)
self._layout.addWidget(self.tabs)
self.editor = MarkdownEdit(self)
self.editor.smarten_punctuation.connect(self.smarten_punctuation)
self.preview = Preview(self)
self.preview.anchor_clicked.connect(self.link_clicked)
self.tabs.addTab(self.editor, _('&Markdown source'))
self.tabs.addTab(self.preview, _('&Preview'))
self.tabs.currentChanged[int].connect(self.change_tab)
self.layout().setContentsMargins(0, 0, 0, 0)
def link_clicked(self, qurl):
safe_open_url(qurl)
def set_base_url(self, qurl):
self.base_url = qurl
self.preview.base_url = self.base_url
def set_minimum_height_for_editor(self, val):
self.editor.setMinimumHeight(val)
@property
def markdown(self):
return self.editor.toPlainText().strip()
@markdown.setter
def markdown(self, v):
self.editor.setPlainText(str(v or ''))
if self.tab == 'preview':
self.update_preview()
def change_tab(self, index):
if index == 1: # changing to preview
self.update_preview()
def update_preview(self):
html = get_markdown(self.editor.toPlainText().strip())
val = f'''\
<html>
<head></head>
<body class="vertical">
<div>{html}</div>
</body>
<html>'''
self.preview.setHtml(val)
@property
def tab(self):
return 'code' if self.tabs.currentWidget() is self.editor else 'preview'
@tab.setter
def tab(self, val):
self.tabs.setCurrentWidget(self.preview if val == 'preview' else self.editor)
def set_readonly(self, val):
self.editor.setReadOnly(bool(val))
def hide_tabs(self):
self.tabs.tabBar().setVisible(False)
def smarten_punctuation(self):
from calibre.ebooks.conversion.preprocess import smarten_punctuation
markdown = self.markdown
newmarkdown = smarten_punctuation(markdown)
if markdown != newmarkdown:
self.markdown = newmarkdown
# }}}
if __name__ == '__main__':
from calibre.gui2 import Application
app = Application([])
w = Editor()
w.set_base_url(QUrl.fromLocalFile(os.getcwd()))
w.resize(800, 600)
w.setWindowFlag(Qt.WindowType.Dialog)
w.show()
w.markdown = '''\
normal& *italic&* **bold&** ***bold-italic*** `code` [link](https://calibre-ebook.com) <span style="font-weight: bold; color:red">span</span>
> Blockquotes
if '>'+' '*4 in string:
pre_block()
1. list 1
1. list 1.1
2. list 1.2
2. list 2
***
* list
- list
# Headers 1
## Headers 2
### Headers 3
#### Headers 4
##### Headers 5
###### Headers 6
'''
app.exec()
# print(w.markdown)