app.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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. # @Last modified by: bach
  7. # @Last modified time: 21-04-2017
  8. # @License: GPL-V3
  9. import sys, os, shutil, tempfile
  10. from PyQt5 import QtCore
  11. from PyQt5.QtCore import QCoreApplication, QUrl, pyqtSlot, QSettings
  12. from PyQt5.QtGui import QIcon, QKeySequence, QFont, QSyntaxHighlighter
  13. from PyQt5.QtWidgets import QMainWindow, QAction, QWidget, QApplication, QShortcut, QGridLayout, QLabel, QTabWidget, QStackedWidget, QHBoxLayout, QVBoxLayout, QSplitter, QSplitterHandle, QPlainTextEdit, QInputDialog, QLineEdit, QFileDialog, QMessageBox, QPushButton
  14. # from PyQt5.QtNetwork import QNetworkProxyFactory, QNetworkRequest
  15. from PyQt5.QtWebKit import QWebSettings
  16. from PyQt5.QtWebKitWidgets import QWebPage, QWebView, QWebInspector
  17. import json
  18. import git
  19. from classes import server, sasscompiler, design, content, md2html
  20. class Core():
  21. def __init__(self, parent=None):
  22. # restore previous preferences
  23. self.appcwd = os.getcwd()
  24. self.restorePreferences()
  25. self._mw = False
  26. self.temp = tempfile.mkdtemp()
  27. # print(self.temp)
  28. self.tempcwd = False
  29. # if ther's not current project folder from restorepref
  30. # initaite a new temp project
  31. if(self.cwd == None or not os.path.isdir(self.cwd)):
  32. self.cwd = os.path.join(self.temp, 'cwd')
  33. self.tempcwd = True
  34. self.initnewproject()
  35. self.server = server.Server(self)
  36. self.sasscompiler = sasscompiler.Compiler(self)
  37. self.contentcompiler = md2html.Compiler(self)
  38. @property
  39. def mainwindow(self):
  40. return self.mainwindow
  41. @mainwindow.setter
  42. def mainwindow(self, mw):
  43. if not self._mw:
  44. self._mw = mw
  45. if not self.tempcwd:
  46. self._mw.setWindowTitle("Cascade – "+self.cwd)
  47. def restorePreferences(self):
  48. # print("restorePreferences")
  49. settings = QSettings('FiguresLibres', 'Cascade')
  50. # settings.clear()
  51. # print(settings.allKeys())
  52. self.cwd = settings.value('core/cwd', None)
  53. self.dialog_path = settings.value('core/dialog_path', os.path.expanduser('~'))
  54. self.mw_size = settings.value('mainwindow/size', QtCore.QSize(1024, 768))
  55. self.mw_pos = settings.value('mainwindow/pos', QtCore.QPoint(0, 0))
  56. self.mw_curstack = int(settings.value('mainwindow/curstack', 0))
  57. def savePreferences(self):
  58. # print("savePreferences")
  59. settings = QSettings('FiguresLibres', 'Cascade')
  60. # print(settings.allKeys())
  61. if not self.tempcwd:
  62. settings.setValue('core/cwd', self.cwd)
  63. settings.setValue('core/dialog_path', self.dialog_path)
  64. settings.setValue('mainwindow/size', self._mw.size())
  65. settings.setValue('mainwindow/pos', self._mw.pos())
  66. settings.setValue('mainwindow/curstack', self._mw.mainstack.currentIndex())
  67. def initnewproject(self, cwd = None):
  68. if cwd == None :
  69. cwd = self.cwd
  70. else :
  71. self.changeCWD(cwd)
  72. shutil.copytree('templates/newproject', cwd)
  73. self.prefs = json.loads(open(os.path.join(cwd,'.config/prefs.json')).read())
  74. self.summary = json.loads(open(os.path.join(cwd,'.config/summary.json')).read())
  75. self.repository = git.Repo.init(cwd)
  76. self.repository.index.add(['assets','contents','.config'])
  77. self.repository.index.commit("initial commit")
  78. # TODO: set mdtohtml compiler from project md to app index.html
  79. # TODO: embed project styles.scss to app scss frame work
  80. def saveproject(self, cwd = None):
  81. if not cwd == None:
  82. shutil.copytree(self.cwd, cwd)
  83. self.tempcwd = False
  84. self.changeCWD(cwd)
  85. def changeCWD(self, cwd):
  86. self.cwd = cwd
  87. self.server.reload()
  88. self.sasscompiler.reload()
  89. self.contentcompiler.reload()
  90. # if not self.tempcwd:
  91. self._mw.setWindowTitle("Cascade – "+self.cwd)
  92. def quit(self):
  93. self.savePreferences()
  94. shutil.rmtree(self.temp, ignore_errors=True)
  95. QCoreApplication.instance().quit()
  96. class MainWindow(QMainWindow):
  97. def __init__(self, core):
  98. super(MainWindow, self).__init__()
  99. # load core class
  100. self.core = core
  101. self.setWindowTitle("Cascade")
  102. self.setWindowIcon(QIcon(os.path.join(self.core.appcwd,'assets/images/icon.png')))
  103. self.resize(self.core.mw_size)
  104. self.move(self.core.mw_pos)
  105. self.initMenuBar()
  106. self.initMainStack()
  107. self.show()
  108. def initMenuBar(self):
  109. # menu bar
  110. bar = self.menuBar()
  111. file = bar.addMenu("&File")
  112. new = QAction("&New Project",self)
  113. new.setShortcut("Ctrl+n")
  114. file.addAction(new)
  115. open = QAction("&Open",self)
  116. open.setShortcut("Ctrl+o")
  117. file.addAction(open)
  118. self.save_action = QAction("&Save Project as",self)
  119. self.save_action.setShortcut("Ctrl+Shift+s")
  120. file.addAction(self.save_action)
  121. quit = QAction("&Quit",self)
  122. quit.setShortcut("Ctrl+q")
  123. file.addAction(quit)
  124. file.triggered[QAction].connect(self.onfilemenutrigger)
  125. # edit menu
  126. edit = bar.addMenu("&Edit")
  127. # edit.addAction("&copy")
  128. # edit.addAction("&paste")
  129. edit.addAction("&build")
  130. self.reload_action = QAction("&Reload",self)
  131. self.reload_action.setShortcut("Ctrl+r")
  132. edit.addAction(self.reload_action)
  133. edit.addAction("&preferences")
  134. edit.triggered[QAction].connect(self.oneditmenutrigger)
  135. # view menu
  136. view = bar.addMenu("&View")
  137. designview = QAction("&Design",self)
  138. designview.setShortcut("F1")
  139. view.addAction(designview)
  140. contentview = QAction("&Content",self)
  141. contentview.setShortcut("F2")
  142. view.addAction(contentview)
  143. versionview = QAction("&Version",self)
  144. versionview.setShortcut("F3")
  145. view.addAction(versionview)
  146. view.triggered[QAction].connect(self.onviewmenutrigger)
  147. # about menu
  148. about = bar.addMenu("About")
  149. about.addAction("&Website")
  150. def onfilemenutrigger(self, q):
  151. print(q.text()+" is triggered")
  152. if q.text() == "&New Project":
  153. self.newprojectdialogue()
  154. elif q.text() == "&Open":
  155. self.openprojectdialogue()
  156. elif q.text() == "&Save Project as":
  157. self.saveprojectdialogue()
  158. elif q.text() == "&Quit":
  159. self.quit()
  160. def openprojectdialogue(self):
  161. print("open")
  162. def newprojectdialogue(self):
  163. dialog = QFileDialog()
  164. dialog.setFileMode(QFileDialog.Directory)
  165. dialog.setAcceptMode(QFileDialog.AcceptOpen)
  166. projectname = dialog.getSaveFileName(
  167. self,
  168. 'New Project',
  169. self.core.dialog_path
  170. )[0]
  171. # TODO: no file type
  172. try:
  173. head, tail = os.path.split(projectname)
  174. self.core.dialog_path = head
  175. if not os.path.isdir(projectname):
  176. self.core.initnewproject(projectname)
  177. else:
  178. print("folder already exists")
  179. # TODO: check if is cascade folder
  180. except Exception as e:
  181. print('Exception', e)
  182. pass
  183. def saveprojectdialogue(self, quit=False):
  184. dialog = QFileDialog()
  185. dialog.setFileMode(QFileDialog.Directory)
  186. dialog.setAcceptMode(QFileDialog.AcceptOpen)
  187. projectname = dialog.getSaveFileName(
  188. self,
  189. 'Save Project',
  190. self.core.dialog_path
  191. )[0]
  192. # TODO: no file type
  193. try:
  194. head, tail = os.path.split(projectname)
  195. self.core.dialog_path = head
  196. if not os.path.isdir(projectname):
  197. self.core.saveproject(projectname)
  198. if quit:
  199. self.quit()
  200. else:
  201. print("folder already exists")
  202. # TODO: check if is cascade folder
  203. except Exception as e:
  204. print('Exception', e)
  205. pass
  206. def quit(self):
  207. print("Quit")
  208. if self.core.tempcwd:
  209. buttonReply = QMessageBox.question(self, 'Project Not Saved', "Do you want to save your current project before quiting?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Cancel)
  210. if buttonReply == QMessageBox.Yes:
  211. self.saveprojectdialogue(quit=True)
  212. if buttonReply == QMessageBox.No:
  213. self.core.quit()
  214. else:
  215. self.core.quit()
  216. def oneditmenutrigger(self, q):
  217. print(q.text()+" is triggered")
  218. if q.text() == "&Reload":
  219. self.designstack.webkitview.reload()
  220. def onviewmenutrigger(self, q):
  221. print(q.text()+" is triggered")
  222. if q.text() == "&Design":
  223. self.mainstack.setCurrentIndex(0)
  224. elif q.text() == "&Content":
  225. self.mainstack.setCurrentIndex(1)
  226. elif q.text() == "&Version":
  227. self.mainstack.setCurrentIndex(2)
  228. def initMainStack(self):
  229. self.mainstack = QStackedWidget()
  230. self.designstack = design.DesignStack(self.core)
  231. self.contentstack = content.ContentStack(self.core)
  232. self.versionstack = QLabel("Version (git).")
  233. self.mainstack.addWidget(self.designstack)
  234. self.mainstack.addWidget(self.contentstack)
  235. self.mainstack.addWidget(self.versionstack)
  236. self.mainstack.setCurrentIndex(self.core.mw_curstack)
  237. self.setCentralWidget(self.mainstack)
  238. def main():
  239. app = QApplication(sys.argv)
  240. app.setOrganizationName('figli')
  241. app.setApplicationName('Cascade')
  242. core = Core()
  243. mainappwindow = MainWindow(core)
  244. core.mainwindow = mainappwindow
  245. sys.exit(app.exec_())
  246. if __name__ == "__main__":
  247. main()