#!/usr/bin/python # -*- coding: utf-8 -*- # @Author: Bachir Soussi Chiadmi # @Date: 23-05-2017 # @Email: bachir@figureslibres.io # @Filename: design.py # @Last modified by: bach # @Last modified time: 03-06-2017 # @License: GPL-V3 from __future__ import absolute_import, print_function, division, unicode_literals import os, re from PyQt5 import QtCore from PyQt5.QtCore import QUrl, QSettings, QSizeF, Qt from PyQt5.QtGui import QKeySequence, QFont from PyQt5.QtWidgets import (QWidget, QTabWidget, QVBoxLayout, QHBoxLayout, QSplitter, QPlainTextEdit, QShortcut, QPushButton, QCheckBox, QSpinBox, QLabel) from PyQt5.QtWebKit import QWebSettings # TODO import own custom build lib of QtWebKit with ctypes # see aur package qt5-webkit-print for options # https://stackoverflow.com/questions/5081875/ctypes-beginner from PyQt5.QtWebKitWidgets import QWebView, QWebInspector from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter from . import highlighter # _ __ __ _ ___ # | | / /__ / /_| | / (_)__ _ __ # | | /| / / _ \/ __ \ | / / / _ \ | /| / / # | |/ |/ / __/ /_/ / |/ / / __/ |/ |/ / # |__/|__/\___/_.___/|___/_/\___/|__/|__/ class WebkitView(QWebView): def __init__(self, parent, core): self.parent = parent self.core = core self.port = core.server.port self.view = QWebView.__init__(self, parent) self.setZoomFactor(1) self.loadFinished.connect(self.onLoaded) self.load(QUrl('http://localhost:'+str(self.port))) self.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) # self.settings().setAttribute(QWebSettings.PluginsEnabled, True) self.initPDF() # self.mainframe = self.page.mainFrame() # print(self.mainframe) def onLoaded(self): print("WebView : onLoaded") self.parent.webviewtoolbar.onRefresh() def initPDF(self): # from https://gist.github.com/gciotta/7766803 self.printer = QPrinter(QPrinter.HighResolution) self.printer.setFullPage(True) self.printer.setPageMargins(0,0,0,0,QPrinter.Millimeter) self.printer.setFontEmbeddingEnabled(True) self.printer.setColorMode(QPrinter.Color) # TODO: set the page size and orientation from doc settings # (need to do doc settings before that) # self.printer.setPageSize(QPrinter.A4) # self.printer.setResolution(96) self.printer.setPaperSize(QSizeF(210, 298), QPrinter.Millimeter) # TODO: change paper size from docsettings # self.printer.setOrientation(QPrinter.Portrait) self.printer.setOutputFormat(QPrinter.PdfFormat) self.printer.setCreator('Libriis') self.printer.setDocName(self.core.projectname) self.printer.setOutputFileName(self.core.projectname+".pdf") # self.setFixedWidth(1000) def ongenPDF(self): # QPrinter::Custom # dialog = QPrintPreviewDialog(self.printer) # dialog.setWindowState(Qt.WindowMaximized) # dialog.paintRequested.connect(self.print_) # dialog.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint | Qt.WindowContextHelpButtonHint) # dialog.exec() # TODO: open a dialogue to ask where to save the pdf # TODO: reload webview and wait for it before printing # TODO: addd a progress bar # self.webview. self.print_(self.printer) def refresh(self): self.initPDF() self.reload() def toggleDocClass(self, c="",a=True): if a : togg = "add" else : togg = "remove" command = """document.documentElement.classList."""+togg+"""('"""+c+"""')""" self.evaluateJS(command) def zoom(self,z): self.setZoomFactor(z/100) # command = """ # var zoomLevel = """+str(z)+""" / 100; # var elt = document.documentElement.querySelector("#pages"); # elt.style.webkitTransform = "scale(" + zoomLevel + ")"; # elt.style.webkitTransformOrigin = "0 0"; # """ # self.evaluateJS(command) def changePage(self,p=0): command = """ var pageNumber = """+str(p-1)+"""; var target = document.documentElement.querySelectorAll('.paper')[pageNumber]; var offsetTop = target.offsetTop; var offsetLeft = target.offsetLeft; document.documentElement.querySelector('body').scrollTop = offsetTop; document.documentElement.querySelector('body').scrollLeft = offsetLeft; """ self.evaluateJS(command) def evaluateJS(self, command): self.page().mainFrame().evaluateJavaScript(command) # ____ __ # / _/___ _________ ___ _____/ /_____ _____ # / // __ \/ ___/ __ \/ _ \/ ___/ __/ __ \/ ___/ # _/ // / / (__ ) /_/ / __/ /__/ /_/ /_/ / / # /___/_/ /_/____/ .___/\___/\___/\__/\____/_/ # /_/ class WebkitInspector(QWebInspector): def __init__(self, parent, webkitview): super(WebkitInspector, self).__init__(parent) self.webkitview = webkitview self.setPage(self.webkitview.page()) self.showMaximized() # TODO: webkitinspector is disappearing when chaging tabs # ______ ______ # /_ __/___ ____ / / __ )____ ______ # / / / __ \/ __ \/ / __ / __ `/ ___/ # / / / /_/ / /_/ / / /_/ / /_/ / / # /_/ \____/\____/_/_____/\__,_/_/ class WebViewToolBar(QWidget): def __init__(self, parent): super(WebViewToolBar, self).__init__(parent) self.parent = parent font = QFont() # font.setFamily("Droid Sans Mono") # font.setFixedPitch(True) font.setPointSize(8) self.setFont(font) self.hbox = QHBoxLayout() self.hbox.setContentsMargins(0,0,0,0) self.preview = QCheckBox('Prev&iew', self) self.preview.stateChanged.connect(self.onPreview) self.hbox.addWidget(self.preview) self.debug = QCheckBox('Deb&ug', self) self.debug.stateChanged.connect(self.onDebug) self.hbox.addWidget(self.debug) self.grid = QCheckBox('&Grid', self) self.grid.stateChanged.connect(self.onGrid) self.hbox.addWidget(self.grid) self.spread = QCheckBox('&Spread', self) self.spread.stateChanged.connect(self.onSpread) self.hbox.addWidget(self.spread) self.facing = QCheckBox('Fa&cing', self) self.facing.stateChanged.connect(self.onFacing) self.hbox.addWidget(self.facing) # self.hbox.addStretch() # # zoom self.hbox.addWidget(QLabel("Zoom:")) self.zoom = QSpinBox(self) self.zoom.setMinimum(-100) self.zoom.setMaximum(200) self.zoom.setSingleStep(10) self.zoom.setValue(90) self.zoom.valueChanged.connect(self.onZoomChanged) self.hbox.addWidget(self.zoom) # page self.gotopage = QLabel("Go to Page: /"+str(self.parent.core.docsettings['np'])) self.hbox.addWidget(self.gotopage) self.page = QSpinBox(self) self.page.setMinimum(1) self.page.setMaximum(int(self.parent.core.docsettings['np'])) self.page.valueChanged.connect(self.onChangePage) self.hbox.addWidget(self.page) self.addpage = QPushButton("&Add Page", self) self.addpage.clicked.connect(self.onAddPage) self.hbox.addWidget(self.addpage) self.rmpage = QPushButton("Re&move Page", self) self.rmpage.clicked.connect(self.onRmPage) self.hbox.addWidget(self.rmpage) # self.hbox.addStretch() # self.reload = QPushButton("&Reload", self) # self.reload.setShortcut('Ctrl+Shift+r') # TODO: how to define same shortcut in different places # self.reload.setIcon(Icon(ico))) self.reload.clicked.connect(self.onReload) self.hbox.addWidget(self.reload) self.genpdf = QPushButton("&PDF", self) # self.genpdf.setShortcut('Ctrl+Shift+r') # TODO: how to define same shortcut in different places # self.genpdf.setIcon(Icon(ico))) self.genpdf.clicked.connect(self.onGenPDF) self.hbox.addWidget(self.genpdf) self.setLayout(self.hbox) # def onCheckboxAction(self, box): # self.parent.webkitview.toggleDocClass(box, self[box].isChecked()) # self.recToolbarState(box, self[box].isChecked()) def onPreview(self): print('Toolbar : onPreview', self.preview.isChecked()) self.parent.webkitview.toggleDocClass('preview', self.preview.isChecked()) self.recToolbarState('preview', self.preview.isChecked()) def onDebug(self): self.parent.webkitview.toggleDocClass('debug', self.debug.isChecked()) self.recToolbarState('debug', self.debug.isChecked()) def onGrid(self): self.parent.webkitview.toggleDocClass('grid', self.grid.isChecked()) self.recToolbarState('grid', self.grid.isChecked()) def onSpread(self): self.parent.webkitview.toggleDocClass('spread', self.spread.isChecked()) self.recToolbarState('spread', self.spread.isChecked()) def onFacing(self): self.parent.webkitview.toggleDocClass('facing', self.facing.isChecked()) self.recToolbarState('facing', self.facing.isChecked()) def onZoomChanged(self,i): # print("onZoomChanged : "+str(i)) self.parent.webkitview.zoom(i) def onZoomOn(self): # print("onZoomOn") self.zoom.setValue(self.zoom.value()+self.zoom.singleStep()) def onZoomOut(self): # print("onZoomOut") self.zoom.setValue(self.zoom.value()-self.zoom.singleStep()) def onChangePage(self, i): # print("onChangePage : "+str(i)) self.parent.webkitview.changePage(i) def onNextPage(self): # print('onNextPage') self.page.setValue(self.page.value()+self.page.singleStep()) def onPrevPage(self): # print('onPrevPage') self.page.setValue(self.page.value()-self.page.singleStep()) def onAddPage(self): # print("onAddPage") self.parent.core.addPage() def onRmPage(self): # print("onAddPage") self.parent.core.rmPage() def onReload(self): # print("onReload") self.parent.webkitview.reload() def onGenPDF(self): print("onGenPDF") self.parent.webkitview.ongenPDF() def recToolbarState(self, prop, val): # print('recToolbarState : '+prop, val) settings = QSettings('FiguresLibres', 'Libriis') settings.setValue('design/toolbar/'+prop, val) # print('recToolbarState after : '+prop, settings.value('design/toolbar/'+prop)) def onRefresh(self): # apply precedent toolbar state settings = QSettings('FiguresLibres', 'Libriis') self.preview.setChecked(bool(settings.value('design/toolbar/preview', False, type=bool))) self.debug.setChecked(bool(settings.value('design/toolbar/debug', False, type=bool))) self.grid.setChecked(bool(settings.value('design/toolbar/grid', False, type=bool))) self.spread.setChecked(bool(settings.value('design/toolbar/spread', False, type=bool))) self.facing.setChecked(bool(settings.value('design/toolbar/facing', False, type=bool))) # trigger webview changes self.parent.webkitview.toggleDocClass('preview', self.preview.isChecked()) self.parent.webkitview.toggleDocClass('debug', self.debug.isChecked()) self.parent.webkitview.toggleDocClass('grid', self.grid.isChecked()) self.parent.webkitview.toggleDocClass('spread', self.spread.isChecked()) self.gotopage.setText("Go to Page: /"+str(self.parent.core.docsettings['np'])) self.page.setMaximum(int(self.parent.core.docsettings['np'])) self.parent.webkitview.changePage(self.page.value()) # ______ ___ __ # / ____/___/ (_) /_____ _____ # / __/ / __ / / __/ __ \/ ___/ # / /___/ /_/ / / /_/ /_/ / / # /_____/\__,_/_/\__/\____/_/ class CodeEditor(QPlainTextEdit): def __init__(self, parent, core, tabs, file, mode): super(CodeEditor, self).__init__() self.parent = parent self.core = core self.tabs = tabs self.file = file self.mode = mode self.setStyle() self.setText() self.setTabStopWidth(15) self.textChanged.connect(self.onTextChanged) self.save_shortcut = QShortcut(QKeySequence("Ctrl+s"), self) self.save_shortcut.activated.connect(self.save) def setStyle(self): font = QFont() font.setFamily("Droid Sans Mono") font.setFixedPitch(True) font.setPointSize(12) self.setFont(font) self.hl= highlighter.Highlighter(self.document(),self.mode) self.setStyleSheet("QPlainTextEdit { background-color: #282828; }") def setText(self): # try: # self.textChanged.disconnect(self.onTextChanged) # except Exception as e: # print(e) self.filepath = os.path.join(self.core.cwd,self.file) self.clear() self.insertPlainText(open(self.filepath, 'r').read()) self.changed = False def onTextChanged(self): print('textChanged') # print(self.toPlainText()) # open(self.filepath, 'w').write(self.toPlainText()) if not self.changed: self.changed = True i = self.tabs.currentIndex() # self.tabs.setTabText(i, re.sub(r'^\**\s', '', self.tabs.tabText(i))) self.tabs.setTabText(i, "* "+self.tabs.tabText(i)) # TODO: indicate that webview needs to be reloaded def save(self): if self.changed: open(self.filepath, 'w').write(self.toPlainText()) i = self.tabs.currentIndex() self.tabs.setTabText(i, re.sub(r'^\**\s', '', self.tabs.tabText(i))) self.parent.reloadView() self.changed = False # TODO: how to combine file save and project save class Editor(QWidget): def __init__(self, parent): super(Editor, self).__init__() self.parent = parent self.layout = QVBoxLayout(self) self.layout.setContentsMargins(0,0,0,0) # Initialize tab screen self.tabs = QTabWidget() self.scsstab = CodeEditor(self, self.parent.core, self.tabs, 'assets/css/styles.scss', "sass") self.jstab = CodeEditor(self, self.parent.core, self.tabs, 'assets/js/script.js', 'js') # Add tabs self.tabs.addTab(self.scsstab,"scss") self.tabs.addTab(self.jstab,"js") # Add tabs to widget self.layout.addWidget(self.tabs) self.setLayout(self.layout) def refresh(self): self.scsstab.setText() self.jstab.setText() def reloadView(self): self.parent.webkitview.reload() # _____ __ __ # / ___// /_____ ______/ /__ # \__ \/ __/ __ `/ ___/ //_/ # ___/ / /_/ /_/ / /__/ ,< # /____/\__/\__,_/\___/_/|_| class DesignStack(QWidget): def __init__(self, core): super(DesignStack, self).__init__() self.core = core # self.grid = QGridLayout() self.hbox = QHBoxLayout() self.hbox.setContentsMargins(0,0,0,0) self.setLayout(self.hbox) self.webview = QWidget() self.webview.vbox = QVBoxLayout() self.webview.setLayout(self.webview.vbox) self.webview.vbox.setContentsMargins(0,0,0,0) # toolbar self.webviewtoolbar = WebViewToolBar(self) self.webview.vbox.addWidget(self.webviewtoolbar) # webkitview self.webkitview = WebkitView(self, core) # webkitinspector self.webkitinspector = WebkitInspector(self, self.webkitview) # V layout self.vsplitter = QSplitter(QtCore.Qt.Vertical) self.vsplitter.addWidget(self.webkitview) self.vsplitter.addWidget(self.webkitinspector) self.vsplitter.splitterMoved.connect(self.movedSplitter) self.webview.vbox.addWidget(self.vsplitter) # H layout self.hsplitter = QSplitter(QtCore.Qt.Horizontal) self.hsplitter.addWidget(self.webview) # editor self.editor = Editor(self) self.hsplitter.addWidget(self.editor) self.hsplitter.splitterMoved.connect(self.movedSplitter) self.hbox.addWidget(self.hsplitter) self.restorePrefs() def toggleInspector(self): self.webkitinspector.setVisible(not self.webkitinspector.isVisible()) def initShortcuts(self): # inspector shortcut = QShortcut(self) shortcut.setKey("F12") shortcut.activated.connect(self.toggleInspector) self.webkitinspector.setVisible(False) # pages # self.pagenext_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.ShiftModifier+Qt.Key_Right), self) # self.pagenext_shortcut.activated.connect(self.webviewtoolbar.onNextPage) # self.pageprev_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.ShiftModifier+Qt.Key_Left), self) # self.pageprev_shortcut.activated.connect(self.webviewtoolbar.onPrevPage) # zoom # self.zoomon_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.Key_Plus), self) # self.zoomon_shortcut.activated.connect(self.webviewtoolbar.onZoomOn) # self.zoomout_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.Key_Minus), self) # self.zoomout_shortcut.activated.connect(self.webviewtoolbar.onZoomOut) def restorePrefs(self): settings = QSettings('FiguresLibres', 'Libriis') print(settings.value('design/vsplitter/sizes', self.vsplitter.sizes())) vals = settings.value('design/vsplitter/sizes', None) if vals: sizes = [] for size in vals: sizes.append(int(size)) self.vsplitter.setSizes(sizes) vals = settings.value('design/hsplitter/sizes', None) if vals: sizes = [] for size in vals: sizes.append(int(size)) self.hsplitter.setSizes(sizes) def movedSplitter(self): settings = QSettings('FiguresLibres', 'Libriis') # print(self.vsplitter.sizes()) settings.setValue('design/vsplitter/sizes', self.vsplitter.sizes()) settings.setValue('design/hsplitter/sizes', self.hsplitter.sizes()) def refresh(self): self.editor.refresh() self.webkitview.refresh()