design.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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. import os, re
  11. # sys,
  12. from PyQt5 import QtCore
  13. from PyQt5.QtCore import QUrl, QSettings, QSizeF
  14. from PyQt5.QtGui import QKeySequence, QFont
  15. from PyQt5.QtWidgets import QWidget, QTabWidget, QVBoxLayout, QHBoxLayout, QSplitter, QPlainTextEdit, QShortcut, QPushButton, QCheckBox, QSpinBox, QLabel
  16. from PyQt5.QtWebKit import QWebSettings
  17. from PyQt5.QtWebKitWidgets import QWebView, QWebInspector
  18. from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
  19. from classes import highlighter
  20. # _ __ __ _ ___
  21. # | | / /__ / /_| | / (_)__ _ __
  22. # | | /| / / _ \/ __ \ | / / / _ \ | /| / /
  23. # | |/ |/ / __/ /_/ / |/ / / __/ |/ |/ /
  24. # |__/|__/\___/_.___/|___/_/\___/|__/|__/
  25. class WebkitView(QWebView):
  26. def __init__(self, parent, core):
  27. self.parent = parent
  28. self.core = core
  29. self.port = core.server.port
  30. self.view = QWebView.__init__(self, parent)
  31. self.setZoomFactor(1)
  32. self.loadFinished.connect(self.onLoaded)
  33. self.load(QUrl('http://localhost:'+str(self.port)))
  34. self.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
  35. # self.settings().setAttribute(QWebSettings.PluginsEnabled, True)
  36. self.initPDF()
  37. # self.mainframe = self.page.mainFrame()
  38. # print(self.mainframe)
  39. def onLoaded(self):
  40. print("onLoaded")
  41. self.parent.webviewtoolbar.onReload()
  42. def initPDF(self):
  43. self.printer = QPrinter(QPrinter.HighResolution)
  44. self.printer.setFullPage(True)
  45. # self.printer.setPageMargins(0,0,0,0,QPrinter.Millimeter)
  46. self.printer.setFontEmbeddingEnabled(True)
  47. self.printer.setColorMode(QPrinter.Color)
  48. # TODO: set the page size and orientation from doc settings
  49. # (need to do doc settings before that)
  50. # self.printer.setPageSize(QPrinter.A4)
  51. self.printer.setPaperSize(QSizeF(210, 300), QPrinter.Millimeter)
  52. # self.printer.setOrientation(QPrinter.Portrait)
  53. self.printer.setOutputFormat(QPrinter.PdfFormat)
  54. self.printer.setCreator('Cascade')
  55. self.printer.setDocName(self.core.projectname)
  56. self.printer.setOutputFileName(self.core.projectname+".pdf")
  57. # self.setFixedWidth(1000)
  58. def ongenPDF(self):
  59. # QPrinter::Custom
  60. # dialog = QPrintPreviewDialog(self.printer)
  61. # dialog.setWindowState(Qt.WindowMaximized)
  62. # dialog.paintRequested.connect(self.print_)
  63. # dialog.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint | Qt.WindowContextHelpButtonHint)
  64. # dialog.exec()
  65. # TODO: open a dialogue to ask where to save the pdf
  66. # TODO: reload webview and wait for it before printing
  67. # TODO: addd a progress bar
  68. # self.webview.
  69. self.print_(self.printer)
  70. def refresh(self):
  71. self.initPDF()
  72. self.reload()
  73. def toggleDocClass(self, c="",a=True):
  74. if a :
  75. togg = "add"
  76. else :
  77. togg = "remove"
  78. command = """document.documentElement.classList."""+togg+"""('"""+c+"""')"""
  79. self.evaluateJS(command)
  80. def evaluateJS(self, command):
  81. self.page().mainFrame().evaluateJavaScript(command)
  82. # ____ __
  83. # / _/___ _________ ___ _____/ /_____ _____
  84. # / // __ \/ ___/ __ \/ _ \/ ___/ __/ __ \/ ___/
  85. # _/ // / / (__ ) /_/ / __/ /__/ /_/ /_/ / /
  86. # /___/_/ /_/____/ .___/\___/\___/\__/\____/_/
  87. # /_/
  88. class WebkitInspector(QWebInspector):
  89. def __init__(self, parent, webkitview):
  90. super(WebkitInspector, self).__init__(parent)
  91. self.webkitview = webkitview
  92. self.setPage(self.webkitview.page())
  93. self.showMaximized()
  94. # TODO: webkitinspector is disappearing when chaging tabs
  95. # ______ ______
  96. # /_ __/___ ____ / / __ )____ ______
  97. # / / / __ \/ __ \/ / __ / __ `/ ___/
  98. # / / / /_/ / /_/ / / /_/ / /_/ / /
  99. # /_/ \____/\____/_/_____/\__,_/_/
  100. class WebViewToolBar(QWidget):
  101. def __init__(self, parent):
  102. super(WebViewToolBar, self).__init__(parent)
  103. self.parent = parent
  104. font = QFont()
  105. # font.setFamily("Droid Sans Mono")
  106. # font.setFixedPitch(True)
  107. font.setPointSize(8)
  108. self.setFont(font)
  109. self.hbox = QHBoxLayout()
  110. self.hbox.setContentsMargins(0,0,0,0)
  111. self.preview = QCheckBox('Prev&iew', self)
  112. self.preview.stateChanged.connect(self.onPreview)
  113. self.hbox.addWidget(self.preview)
  114. self.debug = QCheckBox('Deb&ug', self)
  115. self.debug.stateChanged.connect(self.onDebug)
  116. self.hbox.addWidget(self.debug)
  117. self.grid = QCheckBox('&Grid', self)
  118. self.grid.stateChanged.connect(self.onGrid)
  119. self.hbox.addWidget(self.grid)
  120. self.spread = QCheckBox('&Spread', self)
  121. self.spread.stateChanged.connect(self.onSpread)
  122. self.hbox.addWidget(self.spread)
  123. #
  124. self.hbox.addStretch()
  125. #
  126. # zoom
  127. self.hbox.addWidget(QLabel("Zoom:"))
  128. self.zoom = QSpinBox(self)
  129. # TODO: action
  130. self.hbox.addWidget(self.zoom)
  131. # page
  132. self.gotopage = QLabel("Go to Page: /"+self.parent.core.docsettings['np'])
  133. # TODO: refresh page number on change
  134. self.hbox.addWidget(self.gotopage)
  135. self.page = QSpinBox(self)
  136. # TODO: action
  137. self.hbox.addWidget(self.page)
  138. self.addpage = QPushButton("&Add Page", self)
  139. self.addpage.clicked.connect(self.onAddPage)
  140. self.hbox.addWidget(self.addpage)
  141. self.rmpage = QPushButton("Re&move Page", self)
  142. self.rmpage.clicked.connect(self.onRmPage)
  143. self.hbox.addWidget(self.rmpage)
  144. #
  145. self.hbox.addStretch()
  146. #
  147. self.reload = QPushButton("&Reload", self)
  148. # self.reload.setShortcut('Ctrl+Shift+r')
  149. # TODO: how to define same shortcut in different places
  150. # self.reload.setIcon(Icon(ico)))
  151. self.reload.clicked.connect(self.onReload)
  152. self.hbox.addWidget(self.reload)
  153. self.genpdf = QPushButton("&PDF", self)
  154. # self.genpdf.setShortcut('Ctrl+Shift+r')
  155. # TODO: how to define same shortcut in different places
  156. # self.genpdf.setIcon(Icon(ico)))
  157. self.genpdf.clicked.connect(self.onGenPDF)
  158. self.hbox.addWidget(self.genpdf)
  159. self.setLayout(self.hbox)
  160. # def onCheckboxAction(self, box):
  161. # self.parent.webkitview.toggleDocClass(box, self[box].isChecked())
  162. # self.recordDocSettings(box, self[box].isChecked())
  163. def onPreview(self):
  164. self.parent.webkitview.toggleDocClass('preview', self.preview.isChecked())
  165. self.recordDocSettings('preview', self.preview.isChecked())
  166. def onDebug(self):
  167. self.parent.webkitview.toggleDocClass('debug', self.debug.isChecked())
  168. self.recordDocSettings('debug', self.debug.isChecked())
  169. def onGrid(self):
  170. self.parent.webkitview.toggleDocClass('grid', self.grid.isChecked())
  171. self.recordDocSettings('grid', self.grid.isChecked())
  172. def onSpread(self):
  173. self.parent.webkitview.toggleDocClass('spread', self.spread.isChecked())
  174. self.recordDocSettings('spread', self.spread.isChecked())
  175. def onAddPage(self):
  176. print("onAddPage")
  177. def onReload(self):
  178. print("onReload")
  179. def onGenPDF(self):
  180. print("onGenPDF")
  181. self.parent.webkitview.ongenPDF()
  182. def recordDocSettings(self, prop, val):
  183. print('recordDocSettings : '+prop, val)
  184. settings = QSettings('FiguresLibres', 'Cascade')
  185. settings.setValue('design/toolbar/'+prop, val)
  186. def onReload(self):
  187. # apply precedent toolbar state
  188. settings = QSettings('FiguresLibres', 'Cascade')
  189. self.preview.setChecked(settings.value('design/toolbar/preview', "false") == "true")
  190. self.debug.setChecked(settings.value('design/toolbar/debug', "false") == "true")
  191. self.grid.setChecked(settings.value('design/toolbar/grid', "false") == "true")
  192. self.spread.setChecked(settings.value('design/toolbar/spread', "false") == "true")
  193. # ______ ___ __
  194. # / ____/___/ (_) /_____ _____
  195. # / __/ / __ / / __/ __ \/ ___/
  196. # / /___/ /_/ / / /_/ /_/ / /
  197. # /_____/\__,_/_/\__/\____/_/
  198. class CodeEditor(QPlainTextEdit):
  199. def __init__(self, core, tabs, file, mode):
  200. super(CodeEditor, self).__init__()
  201. self.core = core
  202. self.tabs = tabs
  203. self.file = file
  204. self.setText()
  205. self.setTabStopWidth(15)
  206. self.hl= highlighter.Highlighter(self.document(),mode)
  207. self.shortcut = QShortcut(QKeySequence("Ctrl+s"), self)
  208. self.shortcut.activated.connect(self.save)
  209. def setText(self):
  210. try:
  211. self.textChanged.disconnect(self.onTextChanged)
  212. except Exception as e:
  213. print(e)
  214. self.filepath = os.path.join(self.core.cwd,self.file)
  215. self.clear()
  216. self.insertPlainText(open(self.filepath, 'r').read())
  217. self.changed = False
  218. self.textChanged.connect(self.onTextChanged)
  219. font = QFont()
  220. font.setFamily("Droid Sans Mono")
  221. font.setFixedPitch(True)
  222. font.setPointSize(12)
  223. self.setFont(font)
  224. # self.highlighter = Highlighter(self.document())
  225. # https://pypi.python.org/pypi/QScintilla/2.9.2
  226. def onTextChanged(self):
  227. # print('textChanged')
  228. # print(self.toPlainText())
  229. # open(self.filepath, 'w').write(self.toPlainText())
  230. if not self.changed:
  231. self.changed = True
  232. i = self.tabs.currentIndex()
  233. self.tabs.setTabText(i, "* "+self.tabs.tabText(i))
  234. # TODO: indicate that webview needs to be reloaded
  235. def save(self):
  236. if self.changed:
  237. open(self.filepath, 'w').write(self.toPlainText())
  238. i = self.tabs.currentIndex()
  239. self.tabs.setTabText(i, re.sub(r'^\*\s', '', self.tabs.tabText(i)))
  240. self.changed = False
  241. # TODO: how to combine file save and project save
  242. class Editor(QWidget):
  243. def __init__(self, parent, core):
  244. super(Editor, self).__init__()
  245. self.core = core
  246. self.layout = QVBoxLayout(self)
  247. self.layout.setContentsMargins(0,0,0,0)
  248. # Initialize tab screen
  249. self.tabs = QTabWidget()
  250. self.scsstab = CodeEditor(core, self.tabs, 'assets/css/styles.scss', "scss")
  251. self.jstab = CodeEditor(core, self.tabs, 'assets/js/script.js', 'js')
  252. # Add tabs
  253. self.tabs.addTab(self.scsstab,"scss")
  254. self.tabs.addTab(self.jstab,"js")
  255. # Add tabs to widget
  256. self.layout.addWidget(self.tabs)
  257. self.setLayout(self.layout)
  258. def refresh(self):
  259. self.scsstab.setText()
  260. self.jstab.setText()
  261. # _____ __ __
  262. # / ___// /_____ ______/ /__
  263. # \__ \/ __/ __ `/ ___/ //_/
  264. # ___/ / /_/ /_/ / /__/ ,<
  265. # /____/\__/\__,_/\___/_/|_|
  266. class DesignStack(QWidget):
  267. def __init__(self, core):
  268. super(DesignStack, self).__init__()
  269. self.core = core
  270. # self.grid = QGridLayout()
  271. self.hbox = QHBoxLayout()
  272. self.hbox.setContentsMargins(0,0,0,0)
  273. self.setLayout(self.hbox)
  274. self.webview = QWidget()
  275. self.webview.vbox = QVBoxLayout()
  276. self.webview.setLayout(self.webview.vbox)
  277. self.webview.vbox.setContentsMargins(0,0,0,0)
  278. # toolbar
  279. self.webviewtoolbar = WebViewToolBar(self)
  280. self.webview.vbox.addWidget(self.webviewtoolbar)
  281. # webkitview
  282. self.webkitview = WebkitView(self, core)
  283. # webkitinspector
  284. self.webkitinspector = WebkitInspector(self, self.webkitview)
  285. shortcut = QShortcut(self)
  286. shortcut.setKey("F12")
  287. shortcut.activated.connect(self.toggleInspector)
  288. self.webkitinspector.setVisible(False)
  289. # V layout
  290. self.vsplitter = QSplitter(QtCore.Qt.Vertical)
  291. self.vsplitter.addWidget(self.webkitview)
  292. self.vsplitter.addWidget(self.webkitinspector)
  293. self.vsplitter.splitterMoved.connect(self.movedSplitter)
  294. self.webview.vbox.addWidget(self.vsplitter)
  295. # H layout
  296. self.hsplitter = QSplitter(QtCore.Qt.Horizontal)
  297. self.hsplitter.addWidget(self.webview)
  298. # editor
  299. self.editor = Editor(self, core)
  300. self.hsplitter.addWidget(self.editor)
  301. self.hsplitter.splitterMoved.connect(self.movedSplitter)
  302. self.hbox.addWidget(self.hsplitter)
  303. self.restorePrefs()
  304. def toggleInspector(self):
  305. self.webkitinspector.setVisible(not self.webkitinspector.isVisible())
  306. def restorePrefs(self):
  307. settings = QSettings('FiguresLibres', 'Cascade')
  308. print(settings.value('design/vsplitter/sizes', self.vsplitter.sizes()))
  309. vals = settings.value('design/vsplitter/sizes', None)
  310. if vals:
  311. sizes = []
  312. for size in vals: sizes.append(int(size))
  313. self.vsplitter.setSizes(sizes)
  314. vals = settings.value('design/hsplitter/sizes', None)
  315. if vals:
  316. sizes = []
  317. for size in vals: sizes.append(int(size))
  318. self.hsplitter.setSizes(sizes)
  319. def movedSplitter(self):
  320. settings = QSettings('FiguresLibres', 'Cascade')
  321. # print(self.vsplitter.sizes())
  322. settings.setValue('design/vsplitter/sizes', self.vsplitter.sizes())
  323. settings.setValue('design/hsplitter/sizes', self.hsplitter.sizes())
  324. def refresh(self):
  325. self.editor.refresh()
  326. self.webkitview.refresh()