design.py 17 KB

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