design.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # @Author: Bachir Soussi Chiadmi <bach>
  4. # @Date: 23-05-2017
  5. # @Email: bachir@figureslibres.io
  6. # @Filename: design.py
  7. # @Last modified by: bach
  8. # @Last modified time: 03-06-2017
  9. # @License: GPL-V3
  10. from __future__ import absolute_import, print_function, division, unicode_literals
  11. import os, re
  12. # sys,
  13. from PyQt5 import QtCore
  14. from PyQt5.QtCore import QUrl, QSettings, QSizeF, Qt
  15. from PyQt5.QtGui import QKeySequence, QFont
  16. from PyQt5.QtWidgets import QWidget, QTabWidget, QVBoxLayout, QHBoxLayout, QSplitter, QPlainTextEdit, QShortcut, QPushButton, QCheckBox, QSpinBox, QLabel
  17. from PyQt5.QtWebKit import QWebSettings
  18. from PyQt5.QtWebKitWidgets import QWebView, QWebInspector
  19. from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
  20. from . import highlighter
  21. # _ __ __ _ ___
  22. # | | / /__ / /_| | / (_)__ _ __
  23. # | | /| / / _ \/ __ \ | / / / _ \ | /| / /
  24. # | |/ |/ / __/ /_/ / |/ / / __/ |/ |/ /
  25. # |__/|__/\___/_.___/|___/_/\___/|__/|__/
  26. class WebkitView(QWebView):
  27. def __init__(self, parent, core):
  28. self.parent = parent
  29. self.core = core
  30. self.port = core.server.port
  31. self.view = QWebView.__init__(self, parent)
  32. self.setZoomFactor(1)
  33. self.loadFinished.connect(self.onLoaded)
  34. self.load(QUrl('http://localhost:'+str(self.port)))
  35. self.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
  36. # self.settings().setAttribute(QWebSettings.PluginsEnabled, True)
  37. self.initPDF()
  38. # self.mainframe = self.page.mainFrame()
  39. # print(self.mainframe)
  40. def onLoaded(self):
  41. print("WebView : onLoaded")
  42. self.parent.webviewtoolbar.onRefresh()
  43. def initPDF(self):
  44. self.printer = QPrinter(QPrinter.HighResolution)
  45. self.printer.setFullPage(True)
  46. # self.printer.setPageMargins(0,0,0,0,QPrinter.Millimeter)
  47. self.printer.setFontEmbeddingEnabled(True)
  48. self.printer.setColorMode(QPrinter.Color)
  49. # TODO: set the page size and orientation from doc settings
  50. # (need to do doc settings before that)
  51. # self.printer.setPageSize(QPrinter.A4)
  52. self.printer.setPaperSize(QSizeF(210, 300), QPrinter.Millimeter)
  53. # self.printer.setOrientation(QPrinter.Portrait)
  54. self.printer.setOutputFormat(QPrinter.PdfFormat)
  55. self.printer.setCreator('Libriis')
  56. self.printer.setDocName(self.core.projectname)
  57. self.printer.setOutputFileName(self.core.projectname+".pdf")
  58. # self.setFixedWidth(1000)
  59. def ongenPDF(self):
  60. # QPrinter::Custom
  61. # dialog = QPrintPreviewDialog(self.printer)
  62. # dialog.setWindowState(Qt.WindowMaximized)
  63. # dialog.paintRequested.connect(self.print_)
  64. # dialog.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint | Qt.WindowContextHelpButtonHint)
  65. # dialog.exec()
  66. # TODO: open a dialogue to ask where to save the pdf
  67. # TODO: reload webview and wait for it before printing
  68. # TODO: addd a progress bar
  69. # self.webview.
  70. self.print_(self.printer)
  71. def refresh(self):
  72. self.initPDF()
  73. self.reload()
  74. def toggleDocClass(self, c="",a=True):
  75. if a :
  76. togg = "add"
  77. else :
  78. togg = "remove"
  79. command = """document.documentElement.classList."""+togg+"""('"""+c+"""')"""
  80. self.evaluateJS(command)
  81. def zoom(self,z):
  82. self.setZoomFactor(z/100)
  83. # command = """
  84. # var zoomLevel = """+str(z)+""" / 100;
  85. # var elt = document.documentElement.querySelector("#pages");
  86. # elt.style.webkitTransform = "scale(" + zoomLevel + ")";
  87. # elt.style.webkitTransformOrigin = "0 0";
  88. # """
  89. # self.evaluateJS(command)
  90. def changePage(self,p=0):
  91. command = """
  92. var pageNumber = """+str(p-1)+""";
  93. var target = document.documentElement.querySelectorAll('.paper')[pageNumber];
  94. var offsetTop = target.offsetTop;
  95. var offsetLeft = target.offsetLeft;
  96. document.documentElement.querySelector('body').scrollTop = offsetTop;
  97. document.documentElement.querySelector('body').scrollLeft = offsetLeft;
  98. """
  99. self.evaluateJS(command)
  100. def evaluateJS(self, command):
  101. self.page().mainFrame().evaluateJavaScript(command)
  102. # ____ __
  103. # / _/___ _________ ___ _____/ /_____ _____
  104. # / // __ \/ ___/ __ \/ _ \/ ___/ __/ __ \/ ___/
  105. # _/ // / / (__ ) /_/ / __/ /__/ /_/ /_/ / /
  106. # /___/_/ /_/____/ .___/\___/\___/\__/\____/_/
  107. # /_/
  108. class WebkitInspector(QWebInspector):
  109. def __init__(self, parent, webkitview):
  110. super(WebkitInspector, self).__init__(parent)
  111. self.webkitview = webkitview
  112. self.setPage(self.webkitview.page())
  113. self.showMaximized()
  114. # TODO: webkitinspector is disappearing when chaging tabs
  115. # ______ ______
  116. # /_ __/___ ____ / / __ )____ ______
  117. # / / / __ \/ __ \/ / __ / __ `/ ___/
  118. # / / / /_/ / /_/ / / /_/ / /_/ / /
  119. # /_/ \____/\____/_/_____/\__,_/_/
  120. class WebViewToolBar(QWidget):
  121. def __init__(self, parent):
  122. super(WebViewToolBar, self).__init__(parent)
  123. self.parent = parent
  124. font = QFont()
  125. # font.setFamily("Droid Sans Mono")
  126. # font.setFixedPitch(True)
  127. font.setPointSize(8)
  128. self.setFont(font)
  129. self.hbox = QHBoxLayout()
  130. self.hbox.setContentsMargins(0,0,0,0)
  131. self.preview = QCheckBox('Prev&iew', self)
  132. self.preview.stateChanged.connect(self.onPreview)
  133. self.hbox.addWidget(self.preview)
  134. self.debug = QCheckBox('Deb&ug', self)
  135. self.debug.stateChanged.connect(self.onDebug)
  136. self.hbox.addWidget(self.debug)
  137. self.grid = QCheckBox('&Grid', self)
  138. self.grid.stateChanged.connect(self.onGrid)
  139. self.hbox.addWidget(self.grid)
  140. self.spread = QCheckBox('&Spread', self)
  141. self.spread.stateChanged.connect(self.onSpread)
  142. self.hbox.addWidget(self.spread)
  143. self.facing = QCheckBox('Fa&cing', self)
  144. self.facing.stateChanged.connect(self.onFacing)
  145. self.hbox.addWidget(self.facing)
  146. #
  147. self.hbox.addStretch()
  148. #
  149. # zoom
  150. self.hbox.addWidget(QLabel("Zoom:"))
  151. self.zoom = QSpinBox(self)
  152. self.zoom.setMinimum(-100)
  153. self.zoom.setMaximum(200)
  154. self.zoom.setSingleStep(10)
  155. self.zoom.setValue(90)
  156. self.zoom.valueChanged.connect(self.onZoomChanged)
  157. self.hbox.addWidget(self.zoom)
  158. # page
  159. self.gotopage = QLabel("Go to Page: /"+str(self.parent.core.docsettings['np']))
  160. self.hbox.addWidget(self.gotopage)
  161. self.page = QSpinBox(self)
  162. self.page.setMinimum(1)
  163. self.page.setMaximum(int(self.parent.core.docsettings['np']))
  164. self.page.valueChanged.connect(self.onChangePage)
  165. self.hbox.addWidget(self.page)
  166. self.addpage = QPushButton("&Add Page", self)
  167. self.addpage.clicked.connect(self.onAddPage)
  168. self.hbox.addWidget(self.addpage)
  169. self.rmpage = QPushButton("Re&move Page", self)
  170. self.rmpage.clicked.connect(self.onRmPage)
  171. self.hbox.addWidget(self.rmpage)
  172. #
  173. self.hbox.addStretch()
  174. #
  175. self.reload = QPushButton("&Reload", self)
  176. # self.reload.setShortcut('Ctrl+Shift+r')
  177. # TODO: how to define same shortcut in different places
  178. # self.reload.setIcon(Icon(ico)))
  179. self.reload.clicked.connect(self.onReload)
  180. self.hbox.addWidget(self.reload)
  181. self.genpdf = QPushButton("&PDF", self)
  182. # self.genpdf.setShortcut('Ctrl+Shift+r')
  183. # TODO: how to define same shortcut in different places
  184. # self.genpdf.setIcon(Icon(ico)))
  185. self.genpdf.clicked.connect(self.onGenPDF)
  186. self.hbox.addWidget(self.genpdf)
  187. self.setLayout(self.hbox)
  188. # def onCheckboxAction(self, box):
  189. # self.parent.webkitview.toggleDocClass(box, self[box].isChecked())
  190. # self.recToolbarState(box, self[box].isChecked())
  191. def onPreview(self):
  192. print('Toolbar : onPreview', self.preview.isChecked())
  193. self.parent.webkitview.toggleDocClass('preview', self.preview.isChecked())
  194. self.recToolbarState('preview', self.preview.isChecked())
  195. def onDebug(self):
  196. self.parent.webkitview.toggleDocClass('debug', self.debug.isChecked())
  197. self.recToolbarState('debug', self.debug.isChecked())
  198. def onGrid(self):
  199. self.parent.webkitview.toggleDocClass('grid', self.grid.isChecked())
  200. self.recToolbarState('grid', self.grid.isChecked())
  201. def onSpread(self):
  202. self.parent.webkitview.toggleDocClass('spread', self.spread.isChecked())
  203. self.recToolbarState('spread', self.spread.isChecked())
  204. def onFacing(self):
  205. self.parent.webkitview.toggleDocClass('facing', self.facing.isChecked())
  206. self.recToolbarState('facing', self.facing.isChecked())
  207. def onZoomChanged(self,i):
  208. # print("onZoomChanged : "+str(i))
  209. self.parent.webkitview.zoom(i)
  210. def onZoomOn(self):
  211. # print("onZoomOn")
  212. self.zoom.setValue(self.zoom.value()+self.zoom.singleStep())
  213. def onZoomOut(self):
  214. # print("onZoomOut")
  215. self.zoom.setValue(self.zoom.value()-self.zoom.singleStep())
  216. def onChangePage(self, i):
  217. # print("onChangePage : "+str(i))
  218. self.parent.webkitview.changePage(i)
  219. def onNextPage(self):
  220. # print('onNextPage')
  221. self.page.setValue(self.page.value()+self.page.singleStep())
  222. def onPrevPage(self):
  223. # print('onPrevPage')
  224. self.page.setValue(self.page.value()-self.page.singleStep())
  225. def onAddPage(self):
  226. # print("onAddPage")
  227. self.parent.core.addPage()
  228. def onRmPage(self):
  229. # print("onAddPage")
  230. self.parent.core.rmPage()
  231. def onReload(self):
  232. # print("onReload")
  233. self.parent.webkitview.reload()
  234. def onGenPDF(self):
  235. print("onGenPDF")
  236. self.parent.webkitview.ongenPDF()
  237. def recToolbarState(self, prop, val):
  238. # print('recToolbarState : '+prop, val)
  239. settings = QSettings('FiguresLibres', 'Libriis')
  240. settings.setValue('design/toolbar/'+prop, val)
  241. # print('recToolbarState after : '+prop, settings.value('design/toolbar/'+prop))
  242. def onRefresh(self):
  243. # apply precedent toolbar state
  244. settings = QSettings('FiguresLibres', 'Libriis')
  245. self.preview.setChecked(bool(settings.value('design/toolbar/preview', False, type=bool)))
  246. self.debug.setChecked(bool(settings.value('design/toolbar/debug', False, type=bool)))
  247. self.grid.setChecked(bool(settings.value('design/toolbar/grid', False, type=bool)))
  248. self.spread.setChecked(bool(settings.value('design/toolbar/spread', False, type=bool)))
  249. self.facing.setChecked(bool(settings.value('design/toolbar/facing', False, type=bool)))
  250. # trigger webview changes
  251. self.parent.webkitview.toggleDocClass('preview', self.preview.isChecked())
  252. self.parent.webkitview.toggleDocClass('debug', self.debug.isChecked())
  253. self.parent.webkitview.toggleDocClass('grid', self.grid.isChecked())
  254. self.parent.webkitview.toggleDocClass('spread', self.spread.isChecked())
  255. self.gotopage.setText("Go to Page: /"+str(self.parent.core.docsettings['np']))
  256. self.page.setMaximum(int(self.parent.core.docsettings['np']))
  257. self.parent.webkitview.changePage(self.page.value())
  258. # ______ ___ __
  259. # / ____/___/ (_) /_____ _____
  260. # / __/ / __ / / __/ __ \/ ___/
  261. # / /___/ /_/ / / /_/ /_/ / /
  262. # /_____/\__,_/_/\__/\____/_/
  263. class CodeEditor(QPlainTextEdit):
  264. def __init__(self, parent, core, tabs, file, mode):
  265. super(CodeEditor, self).__init__()
  266. self.parent = parent
  267. self.core = core
  268. self.tabs = tabs
  269. self.file = file
  270. self.hl= highlighter.Highlighter(self.document(),mode)
  271. self.setText()
  272. self.setTabStopWidth(15)
  273. self.textChanged.connect(self.onTextChanged)
  274. self.save_shortcut = QShortcut(QKeySequence("Ctrl+s"), self)
  275. self.save_shortcut.activated.connect(self.save)
  276. def setText(self):
  277. # try:
  278. # self.textChanged.disconnect(self.onTextChanged)
  279. # except Exception as e:
  280. # print(e)
  281. self.filepath = os.path.join(self.core.cwd,self.file)
  282. self.clear()
  283. self.insertPlainText(open(self.filepath, 'r').read())
  284. self.changed = False
  285. font = QFont()
  286. font.setFamily("Droid Sans Mono")
  287. font.setFixedPitch(True)
  288. font.setPointSize(12)
  289. self.setFont(font)
  290. def onTextChanged(self):
  291. print('textChanged')
  292. # print(self.toPlainText())
  293. # open(self.filepath, 'w').write(self.toPlainText())
  294. if not self.changed:
  295. self.changed = True
  296. i = self.tabs.currentIndex()
  297. # self.tabs.setTabText(i, re.sub(r'^\**\s', '', self.tabs.tabText(i)))
  298. self.tabs.setTabText(i, "* "+self.tabs.tabText(i))
  299. # TODO: indicate that webview needs to be reloaded
  300. def save(self):
  301. if self.changed:
  302. open(self.filepath, 'w').write(self.toPlainText())
  303. i = self.tabs.currentIndex()
  304. self.tabs.setTabText(i, re.sub(r'^\**\s', '', self.tabs.tabText(i)))
  305. self.parent.reloadView()
  306. self.changed = False
  307. # TODO: how to combine file save and project save
  308. class Editor(QWidget):
  309. def __init__(self, parent):
  310. super(Editor, self).__init__()
  311. self.parent = parent
  312. self.layout = QVBoxLayout(self)
  313. self.layout.setContentsMargins(0,0,0,0)
  314. # Initialize tab screen
  315. self.tabs = QTabWidget()
  316. self.scsstab = CodeEditor(self, self.parent.core, self.tabs, 'assets/css/styles.scss', "sass")
  317. self.jstab = CodeEditor(self, self.parent.core, self.tabs, 'assets/js/script.js', 'js')
  318. # Add tabs
  319. self.tabs.addTab(self.scsstab,"scss")
  320. self.tabs.addTab(self.jstab,"js")
  321. # Add tabs to widget
  322. self.layout.addWidget(self.tabs)
  323. self.setLayout(self.layout)
  324. def refresh(self):
  325. self.scsstab.setText()
  326. self.jstab.setText()
  327. def reloadView(self):
  328. self.parent.webkitview.reload()
  329. # _____ __ __
  330. # / ___// /_____ ______/ /__
  331. # \__ \/ __/ __ `/ ___/ //_/
  332. # ___/ / /_/ /_/ / /__/ ,<
  333. # /____/\__/\__,_/\___/_/|_|
  334. class DesignStack(QWidget):
  335. def __init__(self, core):
  336. super(DesignStack, self).__init__()
  337. self.core = core
  338. # self.grid = QGridLayout()
  339. self.hbox = QHBoxLayout()
  340. self.hbox.setContentsMargins(0,0,0,0)
  341. self.setLayout(self.hbox)
  342. self.webview = QWidget()
  343. self.webview.vbox = QVBoxLayout()
  344. self.webview.setLayout(self.webview.vbox)
  345. self.webview.vbox.setContentsMargins(0,0,0,0)
  346. # toolbar
  347. self.webviewtoolbar = WebViewToolBar(self)
  348. self.webview.vbox.addWidget(self.webviewtoolbar)
  349. # webkitview
  350. self.webkitview = WebkitView(self, core)
  351. # webkitinspector
  352. self.webkitinspector = WebkitInspector(self, self.webkitview)
  353. # V layout
  354. self.vsplitter = QSplitter(QtCore.Qt.Vertical)
  355. self.vsplitter.addWidget(self.webkitview)
  356. self.vsplitter.addWidget(self.webkitinspector)
  357. self.vsplitter.splitterMoved.connect(self.movedSplitter)
  358. self.webview.vbox.addWidget(self.vsplitter)
  359. # H layout
  360. self.hsplitter = QSplitter(QtCore.Qt.Horizontal)
  361. self.hsplitter.addWidget(self.webview)
  362. # editor
  363. self.editor = Editor(self)
  364. self.hsplitter.addWidget(self.editor)
  365. self.hsplitter.splitterMoved.connect(self.movedSplitter)
  366. self.hbox.addWidget(self.hsplitter)
  367. self.restorePrefs()
  368. def toggleInspector(self):
  369. self.webkitinspector.setVisible(not self.webkitinspector.isVisible())
  370. def initShortcuts(self):
  371. # inspector
  372. shortcut = QShortcut(self)
  373. shortcut.setKey("F12")
  374. shortcut.activated.connect(self.toggleInspector)
  375. self.webkitinspector.setVisible(False)
  376. # pages
  377. # self.pagenext_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.ShiftModifier+Qt.Key_Right), self)
  378. # self.pagenext_shortcut.activated.connect(self.webviewtoolbar.onNextPage)
  379. # self.pageprev_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.ShiftModifier+Qt.Key_Left), self)
  380. # self.pageprev_shortcut.activated.connect(self.webviewtoolbar.onPrevPage)
  381. # zoom
  382. # self.zoomon_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.Key_Plus), self)
  383. # self.zoomon_shortcut.activated.connect(self.webviewtoolbar.onZoomOn)
  384. # self.zoomout_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.Key_Minus), self)
  385. # self.zoomout_shortcut.activated.connect(self.webviewtoolbar.onZoomOut)
  386. def restorePrefs(self):
  387. settings = QSettings('FiguresLibres', 'Libriis')
  388. print(settings.value('design/vsplitter/sizes', self.vsplitter.sizes()))
  389. vals = settings.value('design/vsplitter/sizes', None)
  390. if vals:
  391. sizes = []
  392. for size in vals: sizes.append(int(size))
  393. self.vsplitter.setSizes(sizes)
  394. vals = settings.value('design/hsplitter/sizes', None)
  395. if vals:
  396. sizes = []
  397. for size in vals: sizes.append(int(size))
  398. self.hsplitter.setSizes(sizes)
  399. def movedSplitter(self):
  400. settings = QSettings('FiguresLibres', 'Libriis')
  401. # print(self.vsplitter.sizes())
  402. settings.setValue('design/vsplitter/sizes', self.vsplitter.sizes())
  403. settings.setValue('design/hsplitter/sizes', self.hsplitter.sizes())
  404. def refresh(self):
  405. self.editor.refresh()
  406. self.webkitview.refresh()