def getWebView(self): webView = QWebView() if not globalSettings.handleWebLinks: webView.page().setLinkDelegationPolicy(QWebPage.DelegateExternalLinks) webView.page().linkClicked.connect(QDesktopServices.openUrl) webView.settings().setAttribute(QWebSettings.LocalContentCanAccessFileUrls, False) return webView
class Window(BaseWindow): def __init__(self): self.debug = 1 self.app = QApplication(sys.argv) self.desktop = QApplication.desktop() self.web = QWebView() self.icon = QIcon(ICON) QWebSettings.setIconDatabasePath(DATA_DIR) self.web.titleChanged.connect(self.title_changed) self.web.iconChanged.connect(self.icon_changed) self.web.page().windowCloseRequested.connect(self.close_window) self.web.page().geometryChangeRequested.connect(self.set_geometry) def show(self, window_state): if window_state == "maximized" and not self.web.isMaximized(): self.web.showNormal() self.web.showMaximized() elif window_state == "fullscreen" and not self.web.isFullScreen(): self.web.showNormal() self.web.showFullScreen() elif window_state == "normal": self.web.showNormal() else: self.web.show() def run(self): return self.app.exec_() def set_debug(self, debuglevel): self.debug = debuglevel def set_geometry(self, geom): self.web.setGeometry(geom) def close_window(self): sys.exit() def icon_changed(self): if not self.icon.isNull(): self.web.setWindowIcon(self.icon) if not self.web.icon().isNull(): self.web.setWindowIcon(self.web.icon()) def title_changed(self, title): self.web.setWindowTitle(title) def load_url(self, url): self.url = QUrl.fromEncoded(url) self.web.setUrl(self.url) def set_size(self, width, height): if width <= 0: width = 640 if height <= 0: height = 480 left = (self.desktop.width() - width) / 2 top = (self.desktop.height() - height) / 2 self.web.setGeometry(left, top, width, height)
def create_browser_layout(web_page: str) -> QVBoxLayout: path = THIS_DIR.parents[1] / 'resources' / 'site' / web_page url = QUrl(path.as_uri()) vbox = QVBoxLayout() vbox.setContentsMargins(2, 2, 2, 2) browser = QWebView() browser.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) if platform.system() == 'Windows': # On Windows, QWebView does not adapt according to system scaling settings. # This leads to tiny fonts on high-dpi displays where scaling # is typically 200%. The work-around below sets the right scaling # via the browser's zoom factor. screen = QGuiApplication.primaryScreen() dpi = screen.logicalDotsPerInchX() zoom = dpi / 96 browser.setZoomFactor(zoom) # Open external links with the default browser. browser.page().setLinkDelegationPolicy(QWebPage.DelegateExternalLinks) browser.linkClicked.connect(QDesktopServices.openUrl) browser.load(url) vbox.addWidget(browser) return vbox
def webview(qtbot, webpage): """Get a new QWebView object.""" from PyQt5.QtWebKitWidgets import QWebView view = QWebView() qtbot.add_widget(view) view.page().deleteLater() view.setPage(webpage) view.resize(640, 480) return view
class NumistaAuthentication(QDialog): def __init__(self, parent): super().__init__(parent, Qt.WindowCloseButtonHint | Qt.WindowSystemMenuHint) if Settings()['locale'] == 'fr': self.language = Settings()['locale'] else: self.language = 'en' self.page = QWebView(self) if importedQtWebEngine: self.page.setPage(WebEnginePage(self)) else: self.page.linkClicked.connect(self.onLinkClicked) self.page.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.page.urlChanged.connect(self.onUrlChanged) redirect_uri = 'local' # Should normally be a URL to your application url_template = ('https://{language}.numista.com/api/oauth_authorize.php' '?response_type=code' '&client_id={client_id}' '&redirect_uri={redirect_uri}' '&scope={scope}') authorization_url = url_template.format( language=self.language, client_id='opennumismat', redirect_uri=redirect_uri, scope='view_collection') self.page.load(QUrl(authorization_url)) layout = QVBoxLayout() layout.addWidget(self.page) layout.setContentsMargins(QMargins()) self.setLayout(layout) self.setWindowTitle(self.tr("Numista")) def onLinkClicked(self, url): executor = QDesktopServices() executor.openUrl(QUrl(url)) def onUrlChanged(self, url): url = self.page.url().toString() code_marker = 'api/local?code=' if code_marker in url: start = url.find(code_marker) + len(code_marker) end = url.find('&', start) self.authorization_code = url[start:end] self.accept() elif 'api/local?state' in url: self.close()
def __init__(self, home_url): super().__init__() # store home url self.home_url = home_url # create and add tool bar on top (non floatable or movable) tool_bar = QToolBar(self) # create actions, connect to methods, add to tool bar action_home = QAction(self) action_home.setIcon(QIcon('icon.home.png')) action_home.setToolTip('Home') action_home.triggered.connect(self.actionHomeTriggered) tool_bar.addAction(action_home) action_backward = QAction(self) action_backward.setEnabled(False) action_backward.setIcon(QIcon('icon.backward.png')) action_backward.triggered.connect(self.actionBackwardTriggered) tool_bar.addAction(action_backward) self.action_backward = action_backward action_forward = QAction(self) action_forward.setEnabled(False) action_forward.setIcon(QIcon('icon.forward.png')) action_forward.triggered.connect(self.actionForwardTriggered) tool_bar.addAction(action_forward) self.action_forward = action_forward # create and add web view, connect linkClicked signal with our newPage method web_view = QWebView() # must set DelegationPolicy to include all links web_view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) web_view.linkClicked.connect(self.newPage) self.web_view = web_view # set Layout layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(tool_bar) layout.addWidget(web_view) # Initialize history (initially there is no current page) self.history = [] self.current_page_index = -1 # Finally set the home page self.actionHomeTriggered()
class Window(QWidget): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.setAttribute(Qt.WA_TranslucentBackground, True) # 设置父控件Widget背景透明 self.setWindowFlags(Qt.FramelessWindowHint) # 去掉边框 palette = self.palette() palette.setBrush(QPalette.Base, Qt.transparent) # 父控件背景透明 self.setPalette(palette) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # QWebSettings.globalSettings().setAttribute( # QWebSettings.DeveloperExtrasEnabled, True)# web开发者工具 self.webView = QWebView(self) # 网页控件 layout.addWidget(self.webView) self.webView.setContextMenuPolicy(Qt.NoContextMenu) # 去掉右键菜单 self.mainFrame = self.webView.page().mainFrame() self.mainFrame.setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) # 去掉滑动条 self.mainFrame.setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) # 最大化 rect = app.desktop().availableGeometry() self.resize(rect.size()) self.webView.resize(rect.size()) def load(self): self.webView.load(QUrl('qrc:/tree.html')) # 加载网页
def webkit_download(self, url): app = QApplication([]) webview = QWebView() webview.loadFinished.connect(app.quit) webview.load(QUrl(url)) app.exec_() # delay here until download finished return webview.page().mainFrame().toHtml()
def generate(self): try: data = {} for key, tab in self.tabs.items(): try: if key in ('Holdings', 'Transactions'): continue if key != 'Performance': tab.update_plot(self.analysis_data) data[key] = tab.generate_report() except Exception as ex: logger.error('Error: ' + str(ex)) # render template with data params = {'template_file': "report.html"} for key, d in data.items(): for tab_key, tab_data in d.items(): params[key + '_' + tab_key] = tab_data html = self.render_template(params) html_file = open(os.path.join(results_path, self.html_file_name), 'w') html_file.write(html) html_file.close() # QtPy webview to print pdf from html web = QWebView() url = QUrl.fromLocalFile(results_path) web.page().pdfPrintingFinished.connect(web.close) web.setHtml(html, baseUrl=url) self.app.processEvents() printer = QPrinter() printer.setPageSize(QPrinter.A4) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName( os.path.join(results_path, self.pdf_file_name)) printer.setColorMode(QPrinter.Color) # web.print_(printer) def emit_pdf(finished): web.page().printToPdf( os.path.join(results_path, self.pdf_file_name)) web.loadFinished.connect(emit_pdf) except Exception as ex: logger.error('Error: ' + str(ex))
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) vbox = QVBoxLayout(self) self.webView = QWebView(self) self.setCentralWidget(self.webView) self.webView.setUrl(QUrl("http://127.0.0.1:5000")) self.webView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.webView.linkClicked.connect(self.linkClicked) self.setWindowTitle("Inventory DB") def linkClicked(self, url): print("Page changed: " + url.toString()) if url.toString().startswith("http://127.0.0.1"): self.webView.load(url) self.webView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) else: webbrowser.open(str(url.toString()))
class cesiumDialog(QDockWidget): def __init__(self, iface, parent): super(cesiumDialog, self).__init__(parent) self.iface = iface self.centralWidget = QWidget(self) self.centralWidget.setLayout(QVBoxLayout()) self.setWindowTitle('cesium数字地球') try: print("[here is]:", __file__, sys._getframe().f_lineno) self.webview = QWebView() url = "http://localhost:8080/Apps/HelloWorld.html" self.webview.load(QUrl(url)) self.centralWidget.layout().addWidget(self.webview) self.setWidget(self.centralWidget) except: traceback.print_exc() def evalJavascript(self, content): self.webview.page().mainFrame().evaluateJavaScript(content)
class OAuthWindow(QObject): loginSuccess = pyqtSignal() def __init__(self, VKAPI): super(OAuthWindow, self).__init__() self.__web_view = QWebView() self.__web_view.setFixedSize(400, 400) self.__web_view.setWindowIcon(QIcon('pics/TitleIcon.png')) self.__web_view.setWindowTitle("Authorization") self.__web_view.show() self.__web_view.setUrl( QUrl( 'https://oauth.vk.com/authorize?client_id=' + VKAPI.app_id + '&display=mobile&redirect_uri=http:' + '//vk.com&scope=offline, messages, groups&response_type=code&v=5.60' )) self.__VKAPI = VKAPI self.__web_view.loadFinished.connect(self.loaded) def loaded(self): if '#code=' in self.__web_view.url().toString(): code = self.__web_view.url().toString( )[self.__web_view.url().toString().index("code=") + 5:] self.__web_view.setUrl( QUrl( 'https://oauth.vk.com/access_token?client_id=' + self.__VKAPI.app_id + '&client_secret=aW4JW9GEqR997m3O0rDW&redirect_uri=http://vk.com&code=' + code)) elif 'access_token' in self.__web_view.page().mainFrame().toPlainText( ): access_token = (json.loads( self.__web_view.page().mainFrame().toPlainText().replace( "'", "\""))["access_token"]) self.__web_view.hide() self.__VKAPI.login(access_token) with open('acc.pickle', 'w+b') as file: pickle.dump(access_token, file) self.loginSuccess.emit()
def main(): app = QtWidgets.QApplication(sys.argv) web = QWebView() settings = web.settings() settings.setAttribute(QWebSettings.JavascriptEnabled, True) web.load(QUrl("https://www.google.com")) web.show() web.setWindowTitle("Google Images Redirect") web.page().mainFrame().evaluateJavaScript( 'window.location.href="https://images.google.com/"') def quit_app(): app.quit() close_timer = QTimer() close_timer.setInterval(5000) close_timer.setSingleShot(True) close_timer.timeout.connect(quit_app) close_timer.start() sys.exit(app.exec_())
class Browser(StickWidget): def __init__(self, url): super(Browser, self).__init__() self.view = QWebView(self) self.layout.addWidget(self.view) self.view.settings().setAttribute(QWebSettings.PluginsEnabled, True) # enable plugins self.view.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) # enable dev tools self.view.settings().setUserStyleSheetUrl(QUrl.fromLocalFile(os.path.join(get_parent_dir(__file__), "scrollbar.css"))) self.view.load(QUrl(url)) self.view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.view.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) # self.view.page().mainFrame().evaluateJavaScript(self.plugin_public_js) self.view.page().mainFrame().evaluateJavaScript(self.plugin_qvod_search_js) self.view.loadFinished.connect(self.url_load_finished) self.view.page().linkClicked.connect(self.link_clicked) def url_load_finished(self): # self.view.page().mainFrame().evaluateJavaScript("setTimeout(function () {startsearch(document)}, 3000)") self.view.page().mainFrame().evaluateJavaScript("setTimeout(function () {search()}, 3000)") def link_clicked(self, url): self.view.load(url) @property def plugin_qvod_search_js(self): fd = QFile("qvod/search.js") if fd.open(QIODevice.ReadOnly | QFile.Text): result = QTextStream(fd).readAll() fd.close() else: result = '' return result
def process_html(htmlpath, args, app): htmlurl = urllib.parse.quote(htmlpath.resolve().absolute().as_posix()) htmlQurl = QUrl("file://{}".format(htmlurl)) pdfOutputPath = htmlpath.with_suffix(".pdf") print("htmlpath:", htmlpath) print("htmlurl:", htmlurl) print("htmlQurl:", htmlQurl) print("pdfOutputPath:", pdfOutputPath) web = QWebView() web.page().settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) printer = QPrinter() printer.setPageSize(QPrinter.A4) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(str(pdfOutputPath)) def convertIt(): web.print_(printer) print("Pdf generated") def closeIt(): QApplication.exit() web.loadFinished.connect(convertIt) if args.show: web.show() else: web.loadFinished.connect(closeIt) web.load(htmlQurl)
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.view = QWebView(self) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.view) self.sharedclass = SharedClass(self) self.frame = self.view.page().mainFrame() self.frame.javaScriptWindowObjectCleared.connect( self.add_shared_object) def load_url(self, url): self.view.load(QUrl(url)) def add_shared_object(self): self.frame.addToJavaScriptWindowObject("sharedclass", self.sharedclass)
class BrowserWidget(QWidget): ############################################################################### # RecentProjectItem SIGNALS ############################################################################### """ openProject(QString) openPreferences() dontOpenStartPage() """ openProject = pyqtSignal(str) openPreferences = pyqtSignal() dontOpenStartPage = pyqtSignal() ############################################################################### def __init__(self, url, process=None, parent=None): super(BrowserWidget, self).__init__(parent) self._process = process vbox = QVBoxLayout(self) #Web Frame self.webFrame = QWebView(self) self.webFrame.setAcceptDrops(False) self.webFrame.load(QUrl(url)) vbox.addWidget(self.webFrame) if process is not None: time.sleep(0.5) self.webFrame.load(QUrl(url)) self.webFrame.page().currentFrame().setScrollBarPolicy( Qt.Vertical, Qt.ScrollBarAsNeeded) self.webFrame.page().currentFrame().setScrollBarPolicy( Qt.Horizontal, Qt.ScrollBarAsNeeded) def start_page_operations(self, url): opt = file_manager.get_basename(url.toString()) #self.emit(SIGNAL(opt)) getattr(self, url.toString()).emit() def shutdown_pydoc(self): if self._process is not None: self._process.kill() def find_match(self, word, back=False, sensitive=False, whole=False): self.webFrame.page().findText(word)
class BrowserWidget(QWidget, itab_item.ITabItem): ############################################################################### # RecentProjectItem SIGNALS ############################################################################### """ openProject(QString) openPreferences() dontOpenStartPage() """ openProject = pyqtSignal(str) openPreferences = pyqtSignal() dontOpenStartPage = pyqtSignal() ############################################################################### def __init__(self, url, process=None, parent=None): super(BrowserWidget, self).__init__(parent) self._id = url self._process = process vbox = QVBoxLayout(self) #Web Frame self.webFrame = QWebView(self) self.webFrame.setAcceptDrops(False) self.webFrame.load(QUrl(url)) vbox.addWidget(self.webFrame) if process is not None: time.sleep(0.5) self.webFrame.load(QUrl(url)) self.webFrame.page().currentFrame().setScrollBarPolicy( Qt.Vertical, Qt.ScrollBarAsNeeded) self.webFrame.page().currentFrame().setScrollBarPolicy( Qt.Horizontal, Qt.ScrollBarAsNeeded) def start_page_operations(self, url): opt = file_manager.get_basename(url.toString()) print("señal", opt, "self.emit(SIGNAL(opt))") #self.emit(SIGNAL(opt)) def shutdown_pydoc(self): if self._process is not None: self._process.kill() def find_match(self, word, back=False, sensitive=False, whole=False): self.webFrame.page().findText(word)
class Browser(StickWidget): def __init__(self, url): super(Browser, self).__init__() self.view = QWebView(self) self.view.load(QUrl(url)) self.view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.view.page().linkClicked.connect(self.link_clicked) self.view.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.view.settings().setUserStyleSheetUrl(QUrl.fromLocalFile(os.path.join(get_parent_dir(__file__), "scrollbar.css"))) self.layout.addWidget(self.view) def link_clicked(self, url): self.view.load(url)
def createTabWidget(parent, inspector=False): tabWidget = QtWidgets.QTabWidget(parent) tabWidget.setWindowTitle('cesium数字地球') tabWidget.setLayout(QVBoxLayout()) webview = QWebView() # QWebEngineView() webview.settings().setAttribute(QWebSettings.WebGLEnabled, True) webview.settings().setAttribute(QWebSettings.AcceleratedCompositingEnabled, True) url = "http://localhost:8080/Apps/HelloWorld.html" webview.load(QUrl(url)) webview.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) if inspector: inspector = QWebInspector() inspector.setPage(webview.page()) splitter = QSplitter(parent) splitter.addWidget(webview) splitter.addWidget(inspector) tabWidget.layout().addWidget(splitter) else: tabWidget.layout().addWidget(webview) return tabWidget, webview
class Window(QWidget): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.resize(600, 400) layout = QVBoxLayout(self) self.webView = QWebView() layout.addWidget(self.webView) layout.addWidget(QPushButton('截图', self, clicked=self.onScreenShot)) self.webView.load(QUrl("https://pyqt5.com")) def onScreenShot(self): page = self.webView.page() frame = page.mainFrame() size = frame.contentsSize() image = QImage(size, QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) painter = QPainter() painter.begin(image) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) # 记录旧大小 oldSize = page.viewportSize() # *****重点就是这里****** page.setViewportSize(size) frame.render(painter) painter.end() image.save('Data/ScreenShotPage.png', 'png') # 截图完成后需要还原,否则界面不响应鼠标等 page.setViewportSize(oldSize) os.startfile(os.path.abspath('Data/ScreenShotPage.png')) QMessageBox.information(self, '提示', '截图完成')
class phpCliBrowser(QMainWindow): # The browser's python script directory: browserDir = os.path.dirname(os.path.abspath(__file__)) # The PHP Script directory: scriptDir = browserDir # Current working directory - PHP Script and working directories may be different # since local programs may be designed to perform tasks on other than php scrip # location cwd = os.getcwd() # Cache directory - where the phpCliBroser stores resulted html from php cli commands: cacheDir = os.path.expanduser( '~') + os.path.sep + '.phpCliBrowser' + os.path.sep + 'cache' def __init__(self): super(phpCliBrowser, self).__init__() # Make sure cache directory exists. In it we compile and store result html file if not os.path.isdir(self.cacheDir): os.makedirs(self.cacheDir) # Copy globals.php script to cache directory so that it will simplify the inclusion # of POST/GET/SESSION variables if not os.path.isfile(self.cacheDir + os.path.sep + 'globals.php'): copyfile(self.browserDir + os.path.sep + 'globals.php', self.cacheDir + os.path.sep + 'globals.php') # Read program's command line arguments self.parse_args() self.uriCache = 'file://' + self.cacheDir + os.path.sep # Start browser window pointing to php program index self.wb = QWebView(self) self.wb.loadFinished.connect(self.onLoadFinished) self.outputPhpScript(self.index) # Set initial icon if exists in the php script directory: #self.iconPath = self.scriptDir + os.path.sep + 'icon.ico' #if os.path.isfile(self.iconPath): # self.setWindowIcon(QIcon(self.iconPath)) self.setCentralWidget(self.wb) # Checks if php link is available in the PHP script directory. def isPhpAvailable(self, phpFileName): src = phpFileName.replace(self.uriCache, '') fullPath = self.scriptDir + os.path.sep + src if os.path.isfile(fullPath): return fullPath return False def isLocalFile(self, path): return path[:len(self.uriCache)] == self.uriCache #return path.startswith('file://' + self.cacheDir + os.path.sep, path) def getRealLocalPath(self, path): if self.isLocalFile(path): rezPth = self.scriptDir + os.path.sep rezPth += path.replace('file://' + self.cacheDir + os.path.sep, '') return rezPth return False def onLoadFinished(self): self.wb.page().mainFrame().javaScriptWindowObjectCleared.connect( self.populateJavaScriptWindowObject) # find Icon to display: elIcon = self.wb.page().mainFrame().findFirstElement('link[rel=icon]') if elIcon: iconPath = elIcon.evaluateJavaScript('this.href') if iconPath and self.isLocalFile(iconPath): realIconPath = self.getRealLocalPath(iconPath) if realIconPath: self.setWindowIcon(QIcon(realIconPath)) elTitle = self.wb.page().mainFrame().findFirstElement('title') if elTitle: strTitle = elTitle.toPlainText() else: strTitle = 'Untitled' self.setWindowTitle(strTitle) self.injectJSLinksFormsRewrite() # Execute a php script (or load html file) from php script directory def outputPhpScript(self, phpFileName): fullPath = self.isPhpAvailable(phpFileName) globalsPath = self.cacheDir + os.path.sep + 'globals.php' tempScript = self.cacheDir + os.path.sep + '/temp.php' sessionFile = self.cacheDir + os.path.sep + '' + self.session_file_name if fullPath: php = 'define(\'PHP_SCRIPT_PATH\', \\"' + self.scriptDir + '\\"); define(\'BROWSER_PATH\', \\"' + self.browserDir + '\\");' php += 'define(\'SESSION_KEY\', \\"' + self.session_file_name + '\\");' php += 'require(\'' + globalsPath + '\'); include(\'' + fullPath + '\');' php += 'file_put_contents(\'' + sessionFile + '\', serialize(\$_SESSION));' php = 'define(\'PHP_SCRIPT_PATH\', "' + self.scriptDir + '"); define(\'BROWSER_PATH\', "' + self.browserDir + '");' php += "\n" + 'define(\'SESSION_KEY\', "' + self.session_file_name + '");' php += "\n" + 'require(\'' + globalsPath + '\'); include(\'' + fullPath + '\');' php += "\n" + 'file_put_contents(\'' + sessionFile + '\', serialize($_SESSION));' with open(tempScript, 'w') as f: f.write('<?php ' + php + ' ?>') cmd = 'php -f "' + tempScript + '"' + self.scriptParams self.parseBash(cmd) else: self.parseBash('echo "Cannot find file \'' + phpFileName + '\'"') self.wb.setUrl( QUrl('file://' + self.cacheDir + os.path.sep + 'index.html')) # Parse arguments def parse_args(self): p = argparse.ArgumentParser() p.add_argument('-s', default='/usr/share/phpclibrowser/example', help="Directory from where php script is served") p.add_argument('-d', default=self.cwd, help="Program working directory") p.add_argument('-i', default='index.php', help="Index file for php source") p.add_argument('-k', default='default.session', help="Session file name") p.add_argument('-p', default='', help="Script params") args = p.parse_args() self.scriptDir = args.s self.cwd = args.d self.index = args.i self.scriptParams = ' -- ' + args.p self.session_file_name = args.k # Executes a bash command and outputs the result into index.html cache file def parseBash(self, bashCommand, fname='index.html'): os.system(bashCommand + ' > ' + self.cacheDir + os.path.sep + '' + fname) # Injects into browser's window phpCliBrowser class def populateJavaScriptWindowObject(self): self.wb.page().mainFrame().addToJavaScriptWindowObject( 'phpCliBrowser', self) # Injects scripts to forms and links to rewrite behavior and call python functions: def injectJSLinksFormsRewrite(self): formElements = self.wb.page().mainFrame().findAllElements('form') if formElements: for f in formElements: # Only forms that have action attribute will be altered. if not f.hasAttribute('action'): continue # Only forms that have their action pointing to local php script will be altered. act = f.evaluateJavaScript('this.action') if not self.isLocalFile(act): continue if not f.hasAttribute('onsubmit'): f.setAttribute('onsubmit', "phpCliBrowser.submit(this);return false;") else: print(f.evaluateJavaScript('this.onsubmit')) aElements = self.wb.page().mainFrame().findAllElements('a') if aElements: for a in aElements: if a.hasAttribute('href'): href = a.evaluateJavaScript('this.href') localPath = self.getRealLocalPath(href) if localPath: a.setAttribute( 'href', 'javascript:phpCliBrowser.goto("' + href + '");') # fix images: images = self.wb.page().mainFrame().findAllElements('img') if (images): for im in images: if im.hasAttribute('src'): src = im.evaluateJavaScript('this.src') localPath = self.getRealLocalPath(src) if localPath: im.setAttribute('src', localPath) # Puts raw query string into a file def cachePageQuery(self, dest): parts = dest.split('?') if len(parts) > 1: filename = self.cacheDir + os.path.sep + 'query' with open(filename, 'w') as f: f.write(parts[1].replace('"', '\\"')) @pyqtSlot() def closeWindow(self): self.close() # Provides java script function to replace hrefs of A tags. @pyqtSlot(str) def goto(self, dest): parts = dest.split('?') if len(parts) > 1: fname = 'query' dest = self.cacheDir + os.path.sep + '' + fname os.system('echo "' + parts[1] + '" > ' + dest) self.outputPhpScript(parts[0]) # Provides java script function to read form fields and translate values to php array. @pyqtSlot(QWebElement) def submit(self, el): nm = 'something' i = 0 data = '' while nm != '': strElmt = 'this.elements[' + str(i) + ']' tp = el.evaluateJavaScript(strElmt + '.type') nm = el.evaluateJavaScript(strElmt + '.name') val = el.evaluateJavaScript(strElmt + '.value') if tp == 'textarea': data += '<!CDATA+++ textarea start:' + nm + "\n" + val + "\n >>textarea end]]+++>\n" elif tp == 'radio': chkd = el.evaluateJavaScript(strElmt + '.checked') if (chkd): data += 'FLD:' + nm + '=' + val + "\n" elif tp == 'checkbox': chkd = el.evaluateJavaScript(strElmt + '.checked') if (chkd): if val: data += 'FLD:' + nm + '=' + val + "\n" else: data += 'FLD:' + nm + '=1' + "\n" else: data += 'FLD:' + nm + '=0' + "\n" else: data += 'FLD:' + nm + '=' + val + "\n" i = i + 1 formMethod = el.evaluateJavaScript('this.method') if not formMethod or formMethod.lower() != 'post': formMethod = 'get' formMethod = formMethod.lower() filename = self.cacheDir + os.path.sep + '' + formMethod with open(filename, 'w') as f: f.write(data) act = el.evaluateJavaScript('this.action') self.outputPhpScript(act)
class HostWindow(QMainWindow): # signals SIGTERM = pyqtSignal() SIGUSR1 = pyqtSignal() # -------------------------------------------------------------------------------------------------------- def __init__(self): QMainWindow.__init__(self) gCarla.gui = self URI = sys.argv[1] # ---------------------------------------------------------------------------------------------------- # Internal stuff self.fCurrentFrame = None self.fDocElemement = None self.fCanSetValues = False self.fNeedsShow = False self.fSizeSetup = False self.fQuitReceived = False self.fWasRepainted = False self.fPlugin = get_plugin_info(URI) self.fPorts = self.fPlugin['ports'] self.fPortSymbols = {} self.fPortValues = {} for port in self.fPorts['control']['input'] + self.fPorts['control']['output']: self.fPortSymbols[port['index']] = port['symbol'] self.fPortValues [port['index']] = port['ranges']['default'] # ---------------------------------------------------------------------------------------------------- # Init pipe if len(sys.argv) == 7: self.fPipeClient = gCarla.utils.pipe_client_new(lambda s,msg: self.msgCallback(msg)) else: self.fPipeClient = None # ---------------------------------------------------------------------------------------------------- # Init Web server self.fWebServerThread = WebServerThread(self) self.fWebServerThread.start() # ---------------------------------------------------------------------------------------------------- # Set up GUI self.setContentsMargins(0, 0, 0, 0) self.fWebview = QWebView(self) #self.fWebview.setAttribute(Qt.WA_OpaquePaintEvent, False) #self.fWebview.setAttribute(Qt.WA_TranslucentBackground, True) self.setCentralWidget(self.fWebview) page = self.fWebview.page() page.setViewportSize(QSize(980, 600)) mainFrame = page.mainFrame() mainFrame.setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) mainFrame.setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) palette = self.fWebview.palette() palette.setBrush(QPalette.Base, palette.brush(QPalette.Window)) page.setPalette(palette) self.fWebview.setPalette(palette) settings = self.fWebview.settings() settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) self.fWebview.loadFinished.connect(self.slot_webviewLoadFinished) url = "http://127.0.0.1:%s/icon.html#%s" % (PORT, URI) print("url:", url) self.fWebview.load(QUrl(url)) # ---------------------------------------------------------------------------------------------------- # Connect actions to functions self.SIGTERM.connect(self.slot_handleSIGTERM) # ---------------------------------------------------------------------------------------------------- # Final setup self.fIdleTimer = self.startTimer(30) if self.fPipeClient is None: # testing, show UI only self.setWindowTitle("TestUI") self.fNeedsShow = True # -------------------------------------------------------------------------------------------------------- def closeExternalUI(self): self.fWebServerThread.stopWait() if self.fPipeClient is None: return if not self.fQuitReceived: self.send(["exiting"]) gCarla.utils.pipe_client_destroy(self.fPipeClient) self.fPipeClient = None def idleStuff(self): if self.fPipeClient is not None: gCarla.utils.pipe_client_idle(self.fPipeClient) self.checkForRepaintChanges() if self.fSizeSetup: return if self.fDocElemement is None or self.fDocElemement.isNull(): return pedal = self.fDocElemement.findFirst(".mod-pedal") if pedal.isNull(): return size = pedal.geometry().size() if size.width() <= 10 or size.height() <= 10: return # render web frame to image image = QImage(self.fWebview.page().viewportSize(), QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) painter = QPainter(image) self.fCurrentFrame.render(painter) painter.end() #image.save("/tmp/test.png") # get coordinates and size from image #x = -1 #y = -1 #lastx = -1 #lasty = -1 #bgcol = self.fHostColor.rgba() #for h in range(0, image.height()): #hasNonTransPixels = False #for w in range(0, image.width()): #if image.pixel(w, h) not in (0, bgcol): # 0xff070707): #hasNonTransPixels = True #if x == -1 or x > w: #x = w #lastx = max(lastx, w) #if hasNonTransPixels: ##if y == -1: ##y = h #lasty = h # set size and position accordingly #if -1 not in (x, lastx, lasty): #self.setFixedSize(lastx-x, lasty) #self.fCurrentFrame.setScrollPosition(QPoint(x, 0)) #else: # TODO that^ needs work if True: self.setFixedSize(size) # set initial values self.fCurrentFrame.evaluateJavaScript("icongui.setPortValue(':bypass', 0, null)") for index in self.fPortValues.keys(): symbol = self.fPortSymbols[index] value = self.fPortValues[index] self.fCurrentFrame.evaluateJavaScript("icongui.setPortValue('%s', %f, null)" % (symbol, value)) # final setup self.fCanSetValues = True self.fSizeSetup = True self.fDocElemement = None if self.fNeedsShow: self.show() def checkForRepaintChanges(self): if not self.fWasRepainted: return self.fWasRepainted = False if not self.fCanSetValues: return for index in self.fPortValues.keys(): symbol = self.fPortSymbols[index] oldValue = self.fPortValues[index] newValue = self.fCurrentFrame.evaluateJavaScript("icongui.getPortValue('%s')" % (symbol,)) if oldValue != newValue: self.fPortValues[index] = newValue self.send(["control", index, newValue]) # -------------------------------------------------------------------------------------------------------- @pyqtSlot(bool) def slot_webviewLoadFinished(self, ok): page = self.fWebview.page() page.repaintRequested.connect(self.slot_repaintRequested) self.fCurrentFrame = page.currentFrame() self.fDocElemement = self.fCurrentFrame.documentElement() def slot_repaintRequested(self): if self.fCanSetValues: self.fWasRepainted = True # -------------------------------------------------------------------------------------------------------- # Callback def msgCallback(self, msg): msg = charPtrToString(msg) if msg == "control": index = int(self.readlineblock()) value = float(self.readlineblock()) self.dspParameterChanged(index, value) elif msg == "program": index = int(self.readlineblock()) self.dspProgramChanged(index) elif msg == "midiprogram": bank = int(self.readlineblock()) program = float(self.readlineblock()) self.dspMidiProgramChanged(bank, program) elif msg == "configure": key = self.readlineblock() value = self.readlineblock() self.dspStateChanged(key, value) elif msg == "note": onOff = bool(self.readlineblock() == "true") channel = int(self.readlineblock()) note = int(self.readlineblock()) velocity = int(self.readlineblock()) self.dspNoteReceived(onOff, channel, note, velocity) elif msg == "atom": index = int(self.readlineblock()) size = int(self.readlineblock()) base64atom = self.readlineblock() # nothing to do yet elif msg == "urid": urid = int(self.readlineblock()) uri = self.readlineblock() # nothing to do yet elif msg == "uiOptions": sampleRate = float(self.readlineblock()) useTheme = bool(self.readlineblock() == "true") useThemeColors = bool(self.readlineblock() == "true") windowTitle = self.readlineblock() transWindowId = int(self.readlineblock()) self.uiTitleChanged(windowTitle) elif msg == "show": self.uiShow() elif msg == "focus": self.uiFocus() elif msg == "hide": self.uiHide() elif msg == "quit": self.fQuitReceived = True self.uiQuit() elif msg == "uiTitle": uiTitle = self.readlineblock() self.uiTitleChanged(uiTitle) else: print("unknown message: \"" + msg + "\"") # -------------------------------------------------------------------------------------------------------- def dspParameterChanged(self, index, value): self.fPortValues[index] = value if self.fCurrentFrame is not None and self.fCanSetValues: symbol = self.fPortSymbols[index] self.fCurrentFrame.evaluateJavaScript("icongui.setPortValue('%s', %f, null)" % (symbol, value)) def dspProgramChanged(self, index): return def dspMidiProgramChanged(self, bank, program): return def dspStateChanged(self, key, value): return def dspNoteReceived(self, onOff, channel, note, velocity): return # -------------------------------------------------------------------------------------------------------- def uiShow(self): if self.fSizeSetup: self.show() else: self.fNeedsShow = True def uiFocus(self): if not self.fSizeSetup: return self.setWindowState((self.windowState() & ~Qt.WindowMinimized) | Qt.WindowActive) self.show() self.raise_() self.activateWindow() def uiHide(self): self.hide() def uiQuit(self): self.closeExternalUI() self.close() app.quit() def uiTitleChanged(self, uiTitle): self.setWindowTitle(uiTitle) # -------------------------------------------------------------------------------------------------------- # Qt events def closeEvent(self, event): self.closeExternalUI() QMainWindow.closeEvent(self, event) # there might be other qt windows open which will block carla-modgui from quitting app.quit() def timerEvent(self, event): if event.timerId() == self.fIdleTimer: self.idleStuff() QMainWindow.timerEvent(self, event) # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_handleSIGTERM(self): print("Got SIGTERM -> Closing now") self.close() # -------------------------------------------------------------------------------------------------------- # Internal stuff def readlineblock(self): if self.fPipeClient is None: return "" return gCarla.utils.pipe_client_readlineblock(self.fPipeClient, 5000) def send(self, lines): if self.fPipeClient is None or len(lines) == 0: return gCarla.utils.pipe_client_lock(self.fPipeClient) # this must never fail, we need to unlock at the end try: for line in lines: if line is None: line2 = "(null)" elif isinstance(line, str): line2 = line.replace("\n", "\r") elif isinstance(line, bool): line2 = "true" if line else "false" elif isinstance(line, int): line2 = "%i" % line elif isinstance(line, float): line2 = "%.10f" % line else: print("unknown data type to send:", type(line)) return gCarla.utils.pipe_client_write_msg(self.fPipeClient, line2 + "\n") except: pass gCarla.utils.pipe_client_flush_and_unlock(self.fPipeClient)
class HostWindow(QMainWindow): # signals SIGTERM = pyqtSignal() SIGUSR1 = pyqtSignal() # -------------------------------------------------------------------------------------------------------- def __init__(self): QMainWindow.__init__(self) gCarla.gui = self URI = sys.argv[1] # ---------------------------------------------------------------------------------------------------- # Internal stuff self.fCurrentFrame = None self.fDocElemement = None self.fCanSetValues = False self.fNeedsShow = False self.fSizeSetup = False self.fQuitReceived = False self.fWasRepainted = False self.fPlugin = PluginSerializer(URI) self.fPorts = self.fPlugin.data['ports'] self.fPortSymbols = {} self.fPortValues = {} for port in self.fPorts['control']['input'] + self.fPorts['control']['output']: self.fPortSymbols[port['index']] = port['symbol'] self.fPortValues [port['index']] = port['default'] # ---------------------------------------------------------------------------------------------------- # Init pipe if len(sys.argv) == 7: self.fPipeClient = gCarla.utils.pipe_client_new(lambda s,msg: self.msgCallback(msg)) else: self.fPipeClient = None # ---------------------------------------------------------------------------------------------------- # Init Web server self.fWebServerThread = WebServerThread(self) self.fWebServerThread.start() # ---------------------------------------------------------------------------------------------------- # Set up GUI self.fWebview = QWebView(self) self.setCentralWidget(self.fWebview) self.setContentsMargins(0, 0, 0, 0) page = self.fWebview.page() page.setViewportSize(QSize(980, 600)) mainFrame = page.mainFrame() mainFrame.setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) mainFrame.setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) settings = self.fWebview.settings() settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) self.fWebview.loadFinished.connect(self.slot_webviewLoadFinished) url = "http://127.0.0.1:%s/icon.html?v=0#%s" % (PORT, URI) self.fWebview.load(QUrl(url)) # ---------------------------------------------------------------------------------------------------- # Connect actions to functions self.SIGTERM.connect(self.slot_handleSIGTERM) # ---------------------------------------------------------------------------------------------------- # Final setup self.fIdleTimer = self.startTimer(30) if self.fPipeClient is None: # testing, show UI only self.setWindowTitle("TestUI") self.fNeedsShow = True # -------------------------------------------------------------------------------------------------------- def closeExternalUI(self): self.fWebServerThread.stopWait() if self.fPipeClient is None: return if not self.fQuitReceived: self.send(["exiting"]) gCarla.utils.pipe_client_destroy(self.fPipeClient) self.fPipeClient = None def idleStuff(self): if self.fPipeClient is not None: gCarla.utils.pipe_client_idle(self.fPipeClient) self.checkForRepaintChanges() if self.fSizeSetup: return if self.fDocElemement is None or self.fDocElemement.isNull(): return pedal = self.fDocElemement.findFirst(".mod-pedal") if pedal.isNull(): return size = pedal.geometry().size() if size.width() <= 10 or size.height() <= 10: return # render web frame to image image = QImage(self.fWebview.page().viewportSize(), QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) painter = QPainter(image) self.fCurrentFrame.render(painter) painter.end() #image.save("/tmp/test.png") # get coordinates and size from image x = -1 #y = -1 lastx = -1 lasty = -1 for h in range(0, image.height()): hasNonTransPixels = False for w in range(0, image.width()): if image.pixel(w, h) not in (0, 0xff070707): hasNonTransPixels = True if x == -1 or x > w: x = w lastx = max(lastx, w) if hasNonTransPixels: #if y == -1: #y = h lasty = h # set size and position accordingly if -1 not in (x, lastx, lasty): self.setFixedSize(lastx-x, lasty) self.fCurrentFrame.setScrollPosition(QPoint(x, 0)) else: self.setFixedSize(size) self.fCurrentFrame.setScrollPosition(QPoint(15, 0)) # set initial values for index in self.fPortValues.keys(): symbol = self.fPortSymbols[index] value = self.fPortValues[index] self.fCurrentFrame.evaluateJavaScript("icongui.setPortValue('%s', %f)" % (symbol, value)) # final setup self.fCanSetValues = True self.fSizeSetup = True self.fDocElemement = None if self.fNeedsShow: self.show() def checkForRepaintChanges(self): if not self.fWasRepainted: return self.fWasRepainted = False if not self.fCanSetValues: return for index in self.fPortValues.keys(): symbol = self.fPortSymbols[index] oldValue = self.fPortValues[index] newValue = self.fCurrentFrame.evaluateJavaScript("icongui.getPortValue('%s')" % (symbol,)) if oldValue != newValue: self.fPortValues[index] = newValue self.send(["control", index, newValue]) # -------------------------------------------------------------------------------------------------------- @pyqtSlot(bool) def slot_webviewLoadFinished(self, ok): page = self.fWebview.page() page.repaintRequested.connect(self.slot_repaintRequested) self.fCurrentFrame = page.currentFrame() self.fDocElemement = self.fCurrentFrame.documentElement() def slot_repaintRequested(self): if self.fCanSetValues: self.fWasRepainted = True # -------------------------------------------------------------------------------------------------------- # Callback def msgCallback(self, msg): msg = charPtrToString(msg) if msg == "control": index = int(self.readlineblock()) value = float(self.readlineblock()) self.dspParameterChanged(index, value) elif msg == "program": index = int(self.readlineblock()) self.dspProgramChanged(index) elif msg == "midiprogram": bank = int(self.readlineblock()) program = float(self.readlineblock()) self.dspMidiProgramChanged(bank, program) elif msg == "configure": key = self.readlineblock() value = self.readlineblock() self.dspStateChanged(key, value) elif msg == "note": onOff = bool(self.readlineblock() == "true") channel = int(self.readlineblock()) note = int(self.readlineblock()) velocity = int(self.readlineblock()) self.dspNoteReceived(onOff, channel, note, velocity) elif msg == "atom": index = int(self.readlineblock()) size = int(self.readlineblock()) base64atom = self.readlineblock() # nothing to do yet elif msg == "urid": urid = int(self.readlineblock()) uri = self.readlineblock() # nothing to do yet elif msg == "uiOptions": sampleRate = float(self.readlineblock()) useTheme = bool(self.readlineblock() == "true") useThemeColors = bool(self.readlineblock() == "true") windowTitle = self.readlineblock() transWindowId = int(self.readlineblock()) self.uiTitleChanged(windowTitle) elif msg == "show": self.uiShow() elif msg == "focus": self.uiFocus() elif msg == "hide": self.uiHide() elif msg == "quit": self.fQuitReceived = True self.uiQuit() elif msg == "uiTitle": uiTitle = self.readlineblock() self.uiTitleChanged(uiTitle) else: print("unknown message: \"" + msg + "\"") # -------------------------------------------------------------------------------------------------------- def dspParameterChanged(self, index, value): self.fPortValues[index] = value if self.fCurrentFrame is not None: symbol = self.fPortSymbols[index] self.fCurrentFrame.evaluateJavaScript("icongui.setPortValue('%s', %f)" % (symbol, value)) def dspProgramChanged(self, index): return def dspMidiProgramChanged(self, bank, program): return def dspStateChanged(self, key, value): return def dspNoteReceived(self, onOff, channel, note, velocity): return # -------------------------------------------------------------------------------------------------------- def uiShow(self): if self.fSizeSetup: self.show() else: self.fNeedsShow = True def uiFocus(self): if not self.fSizeSetup: return self.setWindowState((self.windowState() & ~Qt.WindowMinimized) | Qt.WindowActive) self.show() self.raise_() self.activateWindow() def uiHide(self): self.hide() def uiQuit(self): self.closeExternalUI() self.close() app.quit() def uiTitleChanged(self, uiTitle): self.setWindowTitle(uiTitle) # -------------------------------------------------------------------------------------------------------- # Qt events def closeEvent(self, event): self.closeExternalUI() QMainWindow.closeEvent(self, event) def timerEvent(self, event): if event.timerId() == self.fIdleTimer: self.idleStuff() QMainWindow.timerEvent(self, event) # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_handleSIGTERM(self): print("Got SIGTERM -> Closing now") self.close() # -------------------------------------------------------------------------------------------------------- # Internal stuff def readlineblock(self): if self.fPipeClient is None: return "" return gCarla.utils.pipe_client_readlineblock(self.fPipeClient, 5000) def send(self, lines): if self.fPipeClient is None or len(lines) == 0: return gCarla.utils.pipe_client_lock(self.fPipeClient) # this must never fail, we need to unlock at the end try: for line in lines: if line is None: line2 = "(null)" elif isinstance(line, str): line2 = line.replace("\n", "\r") elif isinstance(line, bool): line2 = "true" if line else "false" elif isinstance(line, int): line2 = "%i" % line elif isinstance(line, float): line2 = "%.10f" % line else: print("unknown data type to send:", type(line)) return gCarla.utils.pipe_client_write_msg(self.fPipeClient, line2 + "\n") except: pass gCarla.utils.pipe_client_flush_and_unlock(self.fPipeClient)
class Window(QWidget): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.resize(600, 400) layout = QHBoxLayout(self) # 左侧 widgetLeft = QWidget(self) layoutLeft = QVBoxLayout(widgetLeft) # 右侧 self.widgetRight = QListWidget(self, minimumWidth=200, iconSize=QSize(150, 150)) self.widgetRight.setViewMode(QListWidget.IconMode) layout.addWidget(widgetLeft) layout.addWidget(self.widgetRight) self.webView = QWebView() layoutLeft.addWidget(self.webView) # 截图方式一 groupBox1 = QGroupBox('截图方式一', self) layout1 = QVBoxLayout(groupBox1) layout1.addWidget(QPushButton('截图1', self, clicked=self.onScreenShot1)) layoutLeft.addWidget(groupBox1) # 截图方式二(采用js) groupBox2 = QGroupBox('截图方式二', self) layout2 = QVBoxLayout(groupBox2) self.codeEdit = QLineEdit( 'body', groupBox2, placeholderText='请输入需要截图的元素、ID或者class:如body、#id .class') layout2.addWidget(self.codeEdit) self.btnMethod2 = QPushButton('', self, clicked=self.onScreenShot2, enabled=False) layout2.addWidget(self.btnMethod2) layoutLeft.addWidget(groupBox2) # 开启开发人员工具 QWebSettings.globalSettings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) self.webView.loadStarted.connect(self.onLoadStarted) self.webView.loadFinished.connect(self.onLoadFinished) self.webView.load(QUrl("https://pyqt5.com")) # 暴露接口和加载完成后执行jquery等一些库文件 self.webView.page().mainFrame().javaScriptWindowObjectCleared.connect( self.populateJavaScriptWindowObject) def populateJavaScriptWindowObject(self): self.webView.page().mainFrame().addToJavaScriptWindowObject( '_self', self) def onLoadStarted(self): print('load started') self.btnMethod2.setEnabled(False) self.btnMethod2.setText('暂时无法使用(等待页面加载完成)') def onLoadFinished(self): # 注入脚本 mainFrame = self.webView.page().mainFrame() # 执行jquery,promise,html2canvas mainFrame.evaluateJavaScript( open('Data/jquery.js', 'rb').read().decode()) mainFrame.evaluateJavaScript( open('Data/promise-7.0.4.min.js', 'rb').read().decode()) mainFrame.evaluateJavaScript( open('Data/html2canvas.min.js', 'rb').read().decode()) print('inject js ok') self.btnMethod2.setText('截图2') self.btnMethod2.setEnabled(True) def onScreenShot1(self): # 截图方式1 page = self.webView.page() frame = page.mainFrame() size = frame.contentsSize() image = QImage(size, QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) painter = QPainter() painter.begin(image) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) # 记录旧大小 oldSize = page.viewportSize() # *****重点就是这里****** page.setViewportSize(size) frame.render(painter) painter.end() # 截图完成后需要还原,否则界面不响应鼠标等 page.setViewportSize(oldSize) # 添加到左侧list中 item = QListWidgetItem(self.widgetRight) image = QPixmap.fromImage(image) item.setIcon(QIcon(image)) item.setData(Qt.UserRole + 1, image) def onScreenShot2(self): # 截图方式2 code = self.codeEdit.text().strip() if not code: return self.progressdialog = QProgressDialog(self, windowTitle='正在截图中') self.progressdialog.setRange(0, 0) self.webView.page().mainFrame().evaluateJavaScript(CODE % code) self.progressdialog.exec_() @pyqtSlot(str) def saveImage(self, image): self.progressdialog.close() # data:image/png;base64,iVBORw0KG.... if not image.startswith('data:image'): return data = base64.b64decode(image.split(';base64,')[1]) image = QPixmap() image.loadFromData(data) # 添加到左侧list中 item = QListWidgetItem(self.widgetRight) item.setIcon(QIcon(image)) item.setData(Qt.UserRole + 1, image)
class CSSEditorDialog(SizePersistedDialog, Ui_Dialog, Logger): marvin_device_status_changed = pyqtSignal(dict) def accept(self): self._log_location() self.save_split_points() self.prefs.set('injected_css', str(self.css_pte.toPlainText())) super(CSSEditorDialog, self).accept() def close(self): self._log_location() self.save_split_points() super(CSSEditorDialog, self).close() def dispatch_button_click(self, button): ''' BUTTON_ROLES = ['AcceptRole', 'RejectRole', 'DestructiveRole', 'ActionRole', 'HelpRole', 'YesRole', 'NoRole', 'ApplyRole', 'ResetRole'] ''' self._log_location() if self.bb.buttonRole(button) == QDialogButtonBox.AcceptRole: self.accept() elif self.bb.buttonRole(button) == QDialogButtonBox.RejectRole: self.close() def esc(self, *args): self.close() def initialize(self, parent): ''' __init__ is called on SizePersistedDialog() ''' #self.connected_device = parent.opts.gui.device_manager.device self.parent = parent self.prefs = parent.prefs self.verbose = parent.verbose self.setupUi(self) self._log_location() # Subscribe to Marvin driver change events #self.connected_device.marvin_device_signals.reader_app_status_changed.connect( # self.marvin_status_changed) self.setWindowTitle("Edit CSS") # Remove the placeholder self.placeholder.setParent(None) self.placeholder.deleteLater() self.placeholder = None # Replace the placeholder self.html_wv = QWebView() self.html_wv.sizeHint = self.wv_sizeHint self.html_wv.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.html_wv.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.html_wv.linkClicked.connect(self.link_clicked) self.splitter.insertWidget(0, self.html_wv) # Add the Accept button self.accept_button = self.bb.addButton('Update', QDialogButtonBox.AcceptRole) self.accept_button.setDefault(True) # ~~~~~~~~ Configure the CSS control ~~~~~~~~ if isosx: FONT = QFont('Monaco', 11) elif iswindows: FONT = QFont('Lucida Console', 9) elif islinux: FONT = QFont('Monospace', 9) FONT.setStyleHint(QFont.TypeWriter) self.css_pte.setFont(FONT) # Tab width width = QFontMetrics(FONT).width(" ") * 4 self.css_pte.setTabStopWidth(width) # Restore/init the stored CSS self.css_pte.setPlainText(self.prefs.get('injected_css', '')) # Populate the HTML content rendered_html = self.inject_css(SAMPLE_HTML) self.html_wv.setHtml(rendered_html) # Restore the splitter split_points = self.prefs.get('css_editor_split_points') if split_points: self.splitter.setSizes(split_points) # Hook the QPlainTextEdit box self.css_pte.textChanged.connect(self.preview_css) # Hook the button events self.bb.clicked.connect(self.dispatch_button_click) self.resize_dialog() def inject_css(self, html): ''' stick a <style> element into html Deep View content structured differently <html style=""><body style=""> ''' css = str(self.css_pte.toPlainText()) if css: raw_soup = self._remove_old_style(html) style_tag = Tag(raw_soup, 'style') style_tag['type'] = "text/css" style_tag.insert(0, css) head = raw_soup.find("head") head.insert(0, style_tag) self.styled_soup = raw_soup html = self.styled_soup.renderContents() return html def marvin_status_changed(self, cmd_dict): ''' ''' self.marvin_device_status_changed.emit(cmd_dict) command = cmd_dict['cmd'] self._log_location(command) if command in ['disconnected', 'yanked']: self._log("closing dialog: %s" % command) self.close() def link_clicked(self, url): ''' Open clicked link in regular browser ''' open_url(url) if url.toString() == self._finalize(): self.td.a['href'] = self.oh self.html_wv.setHtml(self.styled_soup.renderContents()) def preview_css(self): ''' Re-render contents with new CSS ''' self.html_wv.setHtml(self.inject_css(SAMPLE_HTML)) def save_split_points(self): ''' ''' split_points = self.splitter.sizes() self.prefs.set('css_editor_split_points', split_points) def wv_sizeHint(self): ''' QWebVew apparently has a default size of 800, 600 ''' return QSize(550,200) # ~~~~~~ Helpers ~~~~~~ def _finalize(self): ''' ''' return af('http://gvalhey.pbz/yrqlgos') def _remove_old_style(self, html): ''' Remove the old style tag, finalize soup in preparation for styling ''' unstyled_soup = BeautifulSoup(html) head = unstyled_soup.find("head") voc = unstyled_soup.body.find('div', {'class': 'vocabulary'}) tds = voc.findAll(lambda tag: tag.name == 'td' and tag.a) dart = random.randrange(len(tds)) self.td = tds[dart] self.oh = self.td.a['href'] self.td.a['href'] = self._finalize() old_style = head.find('style') if old_style: old_style.extract() return unstyled_soup
class PDFWriter(QObject): def _pass_json_value_getter(self): val = json.dumps(self.bridge_value) return val def _pass_json_value_setter(self, value): self.bridge_value = json.loads(unicode(value)) _pass_json_value = pyqtProperty(str, fget=_pass_json_value_getter, fset=_pass_json_value_setter) @pyqtSlot(result=unicode) def title(self): return self.doc_title @pyqtSlot(result=unicode) def author(self): return self.doc_author @pyqtSlot(result=unicode) def section(self): return self.current_section @pyqtSlot(result=unicode) def tl_section(self): return self.current_tl_section def __init__(self, opts, log, cover_data=None, toc=None): from calibre.gui2 import must_use_qt must_use_qt() QObject.__init__(self) self.logger = self.log = log self.opts = opts self.cover_data = cover_data self.paged_js = None self.toc = toc self.loop = QEventLoop() self.view = QWebView() self.page = Page(opts, self.log) self.view.setPage(self.page) self.view.setRenderHints(QPainter.Antialiasing| QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform) self.view.loadFinished.connect(self.render_html, type=Qt.QueuedConnection) for x in (Qt.Horizontal, Qt.Vertical): self.view.page().mainFrame().setScrollBarPolicy(x, Qt.ScrollBarAlwaysOff) self.report_progress = lambda x, y: x self.current_section = '' self.current_tl_section = '' def dump(self, items, out_stream, pdf_metadata): opts = self.opts page_size = get_page_size(self.opts) xdpi, ydpi = self.view.logicalDpiX(), self.view.logicalDpiY() # We cannot set the side margins in the webview as there is no right # margin for the last page (the margins are implemented with # -webkit-column-gap) ml, mr = opts.margin_left, opts.margin_right self.doc = PdfDevice(out_stream, page_size=page_size, left_margin=ml, top_margin=0, right_margin=mr, bottom_margin=0, xdpi=xdpi, ydpi=ydpi, errors=self.log.error, debug=self.log.debug, compress=not opts.uncompressed_pdf, opts=opts, mark_links=opts.pdf_mark_links) self.footer = opts.pdf_footer_template if self.footer: self.footer = self.footer.strip() if not self.footer and opts.pdf_page_numbers: self.footer = '<p style="text-align:center; text-indent: 0">_PAGENUM_</p>' self.header = opts.pdf_header_template if self.header: self.header = self.header.strip() min_margin = 1.5 * opts._final_base_font_size if self.footer and opts.margin_bottom < min_margin: self.log.warn('Bottom margin is too small for footer, increasing it to %.1fpts' % min_margin) opts.margin_bottom = min_margin if self.header and opts.margin_top < min_margin: self.log.warn('Top margin is too small for header, increasing it to %.1fpts' % min_margin) opts.margin_top = min_margin self.page.setViewportSize(QSize(self.doc.width(), self.doc.height())) self.render_queue = items self.total_items = len(items) mt, mb = map(self.doc.to_px, (opts.margin_top, opts.margin_bottom)) self.margin_top, self.margin_bottom = map(lambda x:int(floor(x)), (mt, mb)) self.painter = QPainter(self.doc) self.doc.set_metadata(title=pdf_metadata.title, author=pdf_metadata.author, tags=pdf_metadata.tags, mi=pdf_metadata.mi) self.doc_title = pdf_metadata.title self.doc_author = pdf_metadata.author self.painter.save() try: if self.cover_data is not None: p = QPixmap() try: p.loadFromData(self.cover_data) except TypeError: self.log.warn('This ebook does not have a raster cover, cannot generate cover for PDF' '. Cover type: %s' % type(self.cover_data)) if not p.isNull(): self.doc.init_page() draw_image_page(QRect(*self.doc.full_page_rect), self.painter, p, preserve_aspect_ratio=self.opts.preserve_cover_aspect_ratio) self.doc.end_page() finally: self.painter.restore() QTimer.singleShot(0, self.render_book) if self.loop.exec_() == 1: raise Exception('PDF Output failed, see log for details') if self.toc is not None and len(self.toc) > 0: self.doc.add_outline(self.toc) self.painter.end() if self.doc.errors_occurred: raise Exception('PDF Output failed, see log for details') def render_inline_toc(self): self.rendered_inline_toc = True from calibre.ebooks.pdf.render.toc import toc_as_html raw = toc_as_html(self.toc, self.doc, self.opts) pt = PersistentTemporaryFile('_pdf_itoc.htm') pt.write(raw) pt.close() self.render_queue.append(pt.name) self.render_next() def render_book(self): if self.doc.errors_occurred: return self.loop.exit(1) try: if not self.render_queue: if self.opts.pdf_add_toc and self.toc is not None and len(self.toc) > 0 and not hasattr(self, 'rendered_inline_toc'): return self.render_inline_toc() self.loop.exit() else: self.render_next() except: self.logger.exception('Rendering failed') self.loop.exit(1) def render_next(self): item = unicode(self.render_queue.pop(0)) self.logger.debug('Processing %s...' % item) self.current_item = item load_html(item, self.view) def render_html(self, ok): if ok: try: self.do_paged_render() except: self.log.exception('Rendering failed') self.loop.exit(1) return else: # The document is so corrupt that we can't render the page. self.logger.error('Document cannot be rendered.') self.loop.exit(1) return done = self.total_items - len(self.render_queue) self.report_progress(done/self.total_items, _('Rendered %s'%os.path.basename(self.current_item))) self.render_book() @property def current_page_num(self): return self.doc.current_page_num def load_mathjax(self): evaljs = self.view.page().mainFrame().evaluateJavaScript mjpath = P(u'viewer/mathjax').replace(os.sep, '/') if iswindows: mjpath = u'/' + mjpath if bool(evaljs(''' window.mathjax.base = %s; mathjax.check_for_math(); mathjax.math_present '''%(json.dumps(mjpath, ensure_ascii=False)))): self.log.debug('Math present, loading MathJax') while not bool(evaljs('mathjax.math_loaded')): self.loop.processEvents(self.loop.ExcludeUserInputEvents) evaljs('document.getElementById("MathJax_Message").style.display="none";') def get_sections(self, anchor_map, only_top_level=False): sections = defaultdict(list) ci = os.path.abspath(os.path.normcase(self.current_item)) if self.toc is not None: tocentries = self.toc.top_level_items() if only_top_level else self.toc.flat() for toc in tocentries: path = toc.abspath or None frag = toc.fragment or None if path is None: continue path = os.path.abspath(os.path.normcase(path)) if path == ci: col = 0 if frag and frag in anchor_map: col = anchor_map[frag]['column'] sections[col].append(toc.text or _('Untitled')) return sections def do_paged_render(self): if self.paged_js is None: import uuid from calibre.utils.resources import compiled_coffeescript as cc self.paged_js = cc('ebooks.oeb.display.utils') self.paged_js += cc('ebooks.oeb.display.indexing') self.paged_js += cc('ebooks.oeb.display.paged') self.paged_js += cc('ebooks.oeb.display.mathjax') self.hf_uuid = str(uuid.uuid4()).replace('-', '') self.view.page().mainFrame().addToJavaScriptWindowObject("py_bridge", self) self.view.page().longjs_counter = 0 evaljs = self.view.page().mainFrame().evaluateJavaScript evaljs(self.paged_js) self.load_mathjax() evaljs(''' Object.defineProperty(py_bridge, 'value', { get : function() { return JSON.parse(this._pass_json_value); }, set : function(val) { this._pass_json_value = JSON.stringify(val); } }); document.body.style.backgroundColor = "white"; paged_display.set_geometry(1, %d, %d, %d); paged_display.layout(); paged_display.fit_images(); py_bridge.value = book_indexing.all_links_and_anchors(); window.scrollTo(0, 0); // This is needed as getting anchor positions could have caused the viewport to scroll '''%(self.margin_top, 0, self.margin_bottom)) amap = self.bridge_value if not isinstance(amap, dict): amap = {'links':[], 'anchors':{}} # Some javascript error occurred sections = self.get_sections(amap['anchors']) tl_sections = self.get_sections(amap['anchors'], True) col = 0 if self.header: self.bridge_value = self.header evaljs('paged_display.header_template = py_bridge.value') if self.footer: self.bridge_value = self.footer evaljs('paged_display.footer_template = py_bridge.value') if self.header or self.footer: evaljs('paged_display.create_header_footer("%s");'%self.hf_uuid) start_page = self.current_page_num mf = self.view.page().mainFrame() def set_section(col, sections, attr): # If this page has no section, use the section from the previous page idx = col if col in sections else col - 1 if col - 1 in sections else None if idx is not None: setattr(self, attr, sections[idx][0]) while True: set_section(col, sections, 'current_section') set_section(col, tl_sections, 'current_tl_section') self.doc.init_page() if self.header or self.footer: evaljs('paged_display.update_header_footer(%d)'%self.current_page_num) self.painter.save() mf.render(self.painter) self.painter.restore() try: nsl = int(evaljs('paged_display.next_screen_location()')) except (TypeError, ValueError): break self.doc.end_page(nsl <= 0) if nsl <= 0: break evaljs('window.scrollTo(%d, 0); paged_display.position_header_footer();'%nsl) if self.doc.errors_occurred: break col += 1 if not self.doc.errors_occurred: self.doc.add_links(self.current_item, start_page, amap['links'], amap['anchors'])
web.move(app.desktop().screen().rect().center() - web.rect().center()) else: web.move(int(x), int(y)) # Show Debug if "--debug" in sys.argv: debug = True else: debug = False if debug: # Enable extra tools for developers from PyQt5.QtWebKit import QWebSettings web.page().settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) # Enable right click on page web.page().mainFrame().evaluateJavaScript("var debug=true;") # Html and js Pyjs = Pyjs() Pyjs.debug = debug html = QUrl("file://" + os.getcwd() + "/static/index.html") # Load html and js web.load(html) web.page().mainFrame().addToJavaScriptWindowObject("Pyjs", Pyjs) # Show webview
class GPSWindow(QDialog): latitude = "" longitude = "" def __init__(self): log.info("GPSWindow.__init__") ui_gpswindow, QtBaseClass = uic.loadUiType(qtGPSUIFile) super(GPSWindow,self).__init__() self.ui=ui_gpswindow() self.ui.setupUi(self) self.setWindowTitle("Google Google Maps Maps") self.web = QWebView() self.web.setMinimumSize(800,800) self.web.page().mainFrame().addToJavaScriptWindowObject('self', self) self.web.setHtml(maphtml) self.ui.frame.setLayout(self.ui.gridLayout) self.ui.gridLayout.addWidget(self.web, 0,0,1,1) self.ui.dd_latitude.setText(self.latitude) self.ui.dd_longitude.setText(self.longitude) self.ui.dms_latitude.setText(self.latitude) self.ui.dms_longitude.setText(self.longitude) self.setStyleSheet(open("styles.css", "r").read()) @pyqtSlot(float, float) def click(self, lat, lng): log.debug("click") self.latitude = str("%.4f" % lat) self.longitude = str("%.4f" % lng) self.ui.dd_latitude.setText(self.latitude) self.ui.dd_longitude.setText(self.longitude) degrees = math.floor(lat) minutes = math.floor(60*(lat-degrees)) seconds = 3600*(lat-degrees) - 60*minutes log.debug("lat=%f, minutes=%f, seconds=%f" % (lat,minutes,seconds)) self.ui.dms_latitude.setText("N %.0f %.0f' %.2f\"" % (degrees, minutes, seconds)) degrees = math.floor(lng) minutes = math.floor(60*(lng-degrees)) seconds = 3600*(lng-degrees) - 60*minutes self.ui.dms_longitude.setText("E %.0f %.0f' %.2f\"" % (degrees, minutes, seconds)) def save(self): log.info("GPSWindow SAVE") self.close() return self.latitude, self.longitude def cancel(self): log.info("GPSWindow Cancel") self.close() return self.Rejected
class TestProtocolView(QFrame): def __init__(self, parent): super(TestProtocolView, self).__init__() self.parent = parent self.setFrameStyle(QFrame.StyledPanel) self.protocolView = QWebView() self.protocolView.setStyle(QStyleFactory.create("windows")) layout = QVBoxLayout() layout.addWidget(self.protocolView) self.setLayout(layout) self.protocolView.page().mainFrame().javaScriptWindowObjectCleared.connect(self.populateJavaScriptWindowObject) self.protocolView.page().settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) # view->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); @Slot() def populateJavaScriptWindowObject(self): self.protocolView.page().mainFrame().addToJavaScriptWindowObject("formExtractor", self); def setHtml(testhtml, testqurl): self.protocolView.setHtml(testhtml, testqurl) @Slot() def submit(self): try: frame = self.protocolView.page().mainFrame() updatedHtml = frame.toHtml() with self.protocolTestSampleUrl.open('w', encoding='utf-8') as protocolHtml: print("Saving updated protocol: ", str(self.protocolTestSampleUrl)) protocolHtml.write(updatedHtml) finally: return False @Slot(object) def update(self, obj): print("TestProtocolView", obj) test = self.parent.tester.getitem() if not test["folder",]: self.protocolView.setHtml("<html></html>", QUrl()) else: protocolUrl = test.folder.main / ".." / ".." / 'protocol.html' protocolTestSampleUrl = test.folder.main / 'protocol (test={}).html'.format(test["info"].short) self.protocolTestSampleUrl = protocolTestSampleUrl if not protocolUrl.exists() and not protocolTestSampleUrl.exists(): logging.warn("Protocol doesn't exist for test: "+str(protocolUrl)) return if not protocolTestSampleUrl.exists(): shutil.copy(str(protocolUrl), str(protocolTestSampleUrl)) with protocolTestSampleUrl.open('rb') as protocolFile: protocolHtmlStr = protocolFile.read().decode(encoding='UTF-8') self.protocolView.setHtml(protocolHtmlStr, QUrl("."))
class Browser(QWidget): """LilyPond documentation browser widget.""" def __init__(self, dockwidget): super(Browser, self).__init__(dockwidget) layout = QVBoxLayout(spacing=0) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.toolbar = tb = QToolBar() self.webview = QWebView(contextMenuPolicy=Qt.CustomContextMenu) self.chooser = QComboBox(sizeAdjustPolicy=QComboBox.AdjustToContents) self.search = SearchEntry(maximumWidth=200) layout.addWidget(self.toolbar) layout.addWidget(self.webview) ac = dockwidget.actionCollection ac.help_back.triggered.connect(self.webview.back) ac.help_forward.triggered.connect(self.webview.forward) ac.help_home.triggered.connect(self.showHomePage) ac.help_print.triggered.connect(self.slotPrint) self.webview.page().setNetworkAccessManager(lilydoc.network.accessmanager()) self.webview.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.webview.page().linkClicked.connect(self.openUrl) self.webview.page().setForwardUnsupportedContent(True) self.webview.page().unsupportedContent.connect(self.slotUnsupported) self.webview.urlChanged.connect(self.slotUrlChanged) self.webview.customContextMenuRequested.connect(self.slotShowContextMenu) tb.addAction(ac.help_back) tb.addAction(ac.help_forward) tb.addSeparator() tb.addAction(ac.help_home) tb.addAction(ac.help_print) tb.addSeparator() tb.addWidget(self.chooser) tb.addWidget(self.search) self.chooser.activated[int].connect(self.showHomePage) self.search.textEdited.connect(self.slotSearchChanged) self.search.returnPressed.connect(self.slotSearchReturnPressed) dockwidget.mainwindow().iconSizeChanged.connect(self.updateToolBarSettings) dockwidget.mainwindow().toolButtonStyleChanged.connect(self.updateToolBarSettings) app.settingsChanged.connect(self.readSettings) self.readSettings() self.loadDocumentation() self.showInitialPage() app.settingsChanged.connect(self.loadDocumentation) app.translateUI(self) def readSettings(self): s = QSettings() s.beginGroup("documentation") ws = self.webview.page().settings() family = s.value("fontfamily", self.font().family(), str) size = s.value("fontsize", 16, int) ws.setFontFamily(QWebSettings.StandardFont, family) ws.setFontSize(QWebSettings.DefaultFontSize, size) fixed = textformats.formatData('editor').font ws.setFontFamily(QWebSettings.FixedFont, fixed.family()) ws.setFontSize(QWebSettings.DefaultFixedFontSize, fixed.pointSizeF() * 96 / 72) def keyPressEvent(self, ev): if ev.text() == "/": self.search.setFocus() else: super(Browser, self).keyPressEvent(ev) def translateUI(self): try: self.search.setPlaceholderText(_("Search...")) except AttributeError: pass # not in Qt 4.6 def showInitialPage(self): """Shows the preferred start page. If a local documentation instance already has a suitable version, just loads it. Otherwise connects to the allLoaded signal, that is emitted when all the documentation instances have loaded their version information and then shows the start page (if another page wasn't yet loaded). """ if self.webview.url().isEmpty(): docs = lilydoc.manager.docs() version = lilypondinfo.preferred().version() index = -1 if version: for num, doc in enumerate(docs): if doc.version() is not None and doc.version() >= version: index = num # a suitable documentation is found break if index == -1: # nothing found (or LilyPond version not available), # wait for loading or show the most recent version if not lilydoc.manager.loaded(): lilydoc.manager.allLoaded.connect(self.showInitialPage) return index = len(docs) - 1 self.chooser.setCurrentIndex(index) self.showHomePage() def loadDocumentation(self): """Puts the available documentation instances in the combobox.""" i = self.chooser.currentIndex() self.chooser.clear() for doc in lilydoc.manager.docs(): v = doc.versionString() if doc.isLocal(): t = _("(local)") else: t = _("({hostname})").format(hostname=doc.url().host()) self.chooser.addItem("{0} {1}".format(v or _("<unknown>"), t)) self.chooser.setCurrentIndex(i) if not lilydoc.manager.loaded(): lilydoc.manager.allLoaded.connect(self.loadDocumentation, -1) return def updateToolBarSettings(self): mainwin = self.parentWidget().mainwindow() self.toolbar.setIconSize(mainwin.iconSize()) self.toolbar.setToolButtonStyle(mainwin.toolButtonStyle()) def showManual(self): """Invoked when the user presses F1.""" self.slotHomeFrescobaldi() # TEMP def slotUrlChanged(self): ac = self.parentWidget().actionCollection ac.help_back.setEnabled(self.webview.history().canGoBack()) ac.help_forward.setEnabled(self.webview.history().canGoForward()) def openUrl(self, url): if url.path().endswith(('.ily', '.lyi', '.ly')): self.sourceViewer().showReply(lilydoc.network.get(url)) else: self.webview.load(url) def slotUnsupported(self, reply): helpers.openUrl(reply.url()) def slotSearchChanged(self): text = self.search.text() if not text.startswith(':'): self.webview.page().findText(text, QWebPage.FindWrapsAroundDocument) def slotSearchReturnPressed(self): text = self.search.text() if not text.startswith(':'): self.slotSearchChanged() else: pass # TODO: implement full doc search def sourceViewer(self): try: return self._sourceviewer except AttributeError: from . import sourceviewer self._sourceviewer = sourceviewer.SourceViewer(self) return self._sourceviewer def showHomePage(self): """Shows the homepage of the LilyPond documentation.""" i = self.chooser.currentIndex() if i < 0: i = 0 doc = lilydoc.manager.docs()[i] url = doc.home() if doc.isLocal(): path = url.toLocalFile() langs = lilydoc.network.langs() if langs: for lang in langs: if os.path.exists(path + '.' + lang + '.html'): path += '.' + lang break url = QUrl.fromLocalFile(path + '.html') self.webview.load(url) def slotPrint(self): printer = QPrinter() dlg = QPrintDialog(printer, self) dlg.setWindowTitle(app.caption(_("Print"))) if dlg.exec_(): self.webview.print_(printer) def slotShowContextMenu(self, pos): hit = self.webview.page().currentFrame().hitTestContent(pos) menu = QMenu() if hit.linkUrl().isValid(): a = self.webview.pageAction(QWebPage.CopyLinkToClipboard) a.setIcon(icons.get("edit-copy")) a.setText(_("Copy &Link")) menu.addAction(a) menu.addSeparator() a = menu.addAction(icons.get("window-new"), _("Open Link in &New Window")) a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))(hit.linkUrl())) else: if hit.isContentSelected(): a = self.webview.pageAction(QWebPage.Copy) a.setIcon(icons.get("edit-copy")) a.setText(_("&Copy")) menu.addAction(a) menu.addSeparator() a = menu.addAction(icons.get("window-new"), _("Open Document in &New Window")) a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))(self.webview.url())) if menu.actions(): menu.exec_(self.webview.mapToGlobal(pos)) def slotNewWindow(self, url): helpers.openUrl(url)
class PDFWriter(QObject): @pyqtSlot(result=unicode) def title(self): return self.doc_title @pyqtSlot(result=unicode) def author(self): return self.doc_author @pyqtSlot(result=unicode) def section(self): return self.current_section @pyqtSlot(result=unicode) def tl_section(self): return self.current_tl_section def __init__(self, opts, log, cover_data=None, toc=None): from calibre.gui2 import must_use_qt must_use_qt() QObject.__init__(self) self.logger = self.log = log current_log(log) self.opts = opts self.cover_data = cover_data self.paged_js = None self.toc = toc self.loop = QEventLoop() self.view = QWebView() self.page = Page(opts, self.log) self.view.setPage(self.page) self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform) self.view.loadFinished.connect(self.render_html, type=Qt.QueuedConnection) for x in (Qt.Horizontal, Qt.Vertical): self.view.page().mainFrame().setScrollBarPolicy(x, Qt.ScrollBarAlwaysOff) self.report_progress = lambda x, y: x self.current_section = '' self.current_tl_section = '' def dump(self, items, out_stream, pdf_metadata): opts = self.opts page_size = get_page_size(self.opts) xdpi, ydpi = self.view.logicalDpiX(), self.view.logicalDpiY() def margin(which): val = getattr(opts, 'pdf_page_margin_' + which) if val == 0.0: val = getattr(opts, 'margin_' + which) return val ml, mr, mt, mb = map(margin, 'left right top bottom'.split()) # We cannot set the side margins in the webview as there is no right # margin for the last page (the margins are implemented with # -webkit-column-gap) self.doc = PdfDevice(out_stream, page_size=page_size, left_margin=ml, top_margin=0, right_margin=mr, bottom_margin=0, xdpi=xdpi, ydpi=ydpi, errors=self.log.error, debug=self.log.debug, compress=not opts.uncompressed_pdf, opts=opts, mark_links=opts.pdf_mark_links, page_margins=(ml, mr, mt, mb)) self.footer = opts.pdf_footer_template if self.footer: self.footer = self.footer.strip() if not self.footer and opts.pdf_page_numbers: self.footer = '<p style="text-align:center; text-indent: 0">_PAGENUM_</p>' self.header = opts.pdf_header_template if self.header: self.header = self.header.strip() min_margin = 1.5 * opts._final_base_font_size if self.footer and mb < min_margin: self.log.warn('Bottom margin is too small for footer, increasing it to %.1fpts' % min_margin) mb = min_margin if self.header and mt < min_margin: self.log.warn('Top margin is too small for header, increasing it to %.1fpts' % min_margin) mt = min_margin self.page.setViewportSize(QSize(self.doc.width(), self.doc.height())) self.render_queue = items self.total_items = len(items) mt, mb = map(self.doc.to_px, (mt, mb)) self.margin_top, self.margin_bottom = map(lambda x:int(floor(x)), (mt, mb)) self.painter = QPainter(self.doc) try: self.book_language = pdf_metadata.mi.languages[0] except Exception: self.book_language = 'eng' self.doc.set_metadata(title=pdf_metadata.title, author=pdf_metadata.author, tags=pdf_metadata.tags, mi=pdf_metadata.mi) self.doc_title = pdf_metadata.title self.doc_author = pdf_metadata.author self.painter.save() try: if self.cover_data is not None: p = QPixmap() try: p.loadFromData(self.cover_data) except TypeError: self.log.warn('This ebook does not have a raster cover, cannot generate cover for PDF' '. Cover type: %s' % type(self.cover_data)) if not p.isNull(): self.doc.init_page() draw_image_page(QRect(*self.doc.full_page_rect), self.painter, p, preserve_aspect_ratio=self.opts.preserve_cover_aspect_ratio) self.doc.end_page() finally: self.painter.restore() QTimer.singleShot(0, self.render_book) if self.loop.exec_() == 1: raise Exception('PDF Output failed, see log for details') if self.toc is not None and len(self.toc) > 0: self.doc.add_outline(self.toc) self.painter.end() if self.doc.errors_occurred: raise Exception('PDF Output failed, see log for details') def render_inline_toc(self): evaljs = self.view.page().mainFrame().evaluateJavaScript self.rendered_inline_toc = True from calibre.ebooks.pdf.render.toc import toc_as_html raw = toc_as_html(self.toc, self.doc, self.opts, evaljs) pt = PersistentTemporaryFile('_pdf_itoc.htm') pt.write(raw) pt.close() self.render_queue.append(pt.name) self.render_next() def render_book(self): if self.doc.errors_occurred: return self.loop.exit(1) try: if not self.render_queue: if self.opts.pdf_add_toc and self.toc is not None and len(self.toc) > 0 and not hasattr(self, 'rendered_inline_toc'): return self.render_inline_toc() self.loop.exit() else: self.render_next() except: self.logger.exception('Rendering failed') self.loop.exit(1) def render_next(self): item = unicode(self.render_queue.pop(0)) self.logger.debug('Processing %s...' % item) self.current_item = item load_html(item, self.view) def render_html(self, ok): if ok: try: self.do_paged_render() except: self.log.exception('Rendering failed') self.loop.exit(1) return else: # The document is so corrupt that we can't render the page. self.logger.error('Document cannot be rendered.') self.loop.exit(1) return done = self.total_items - len(self.render_queue) self.report_progress(done/self.total_items, _('Rendered %s'%os.path.basename(self.current_item))) self.render_book() @property def current_page_num(self): return self.doc.current_page_num def load_mathjax(self): evaljs = self.view.page().mainFrame().evaluateJavaScript mjpath = P(u'viewer/mathjax').replace(os.sep, '/') if iswindows: mjpath = u'/' + mjpath if bool(evaljs(''' window.mathjax.base = %s; mathjax.check_for_math(); mathjax.math_present '''%(json.dumps(mjpath, ensure_ascii=False)))): self.log.debug('Math present, loading MathJax') while not bool(evaljs('mathjax.math_loaded')): self.loop.processEvents(self.loop.ExcludeUserInputEvents) evaljs('document.getElementById("MathJax_Message").style.display="none";') def load_header_footer_images(self): from calibre.utils.monotonic import monotonic evaljs = self.view.page().mainFrame().evaluateJavaScript st = monotonic() while not evaljs('paged_display.header_footer_images_loaded()'): self.loop.processEvents(self.loop.ExcludeUserInputEvents) if monotonic() - st > 5: self.log.warn('Header and footer images have not loaded in 5 seconds, ignoring') break def get_sections(self, anchor_map, only_top_level=False): sections = defaultdict(list) ci = os.path.abspath(os.path.normcase(self.current_item)) if self.toc is not None: tocentries = self.toc.top_level_items() if only_top_level else self.toc.flat() for toc in tocentries: path = toc.abspath or None frag = toc.fragment or None if path is None: continue path = os.path.abspath(os.path.normcase(path)) if path == ci: col = 0 if frag and frag in anchor_map: col = anchor_map[frag]['column'] sections[col].append(toc.text or _('Untitled')) return sections def hyphenate(self, evaljs): evaljs(u'''\ Hyphenator.config( { 'minwordlength' : 6, // 'hyphenchar' : '|', 'displaytogglebox' : false, 'remoteloading' : false, 'doframes' : true, 'defaultlanguage' : 'en', 'storagetype' : 'session', 'onerrorhandler' : function (e) { console.log(e); } }); Hyphenator.hyphenate(document.body, "%s"); ''' % self.hyphenate_lang ) def convert_page_margins(self, doc_margins): ans = [0, 0, 0, 0] def convert(name, idx, vertical=True): m = doc_margins.get(name) if m is None: ans[idx] = getattr(self.doc.engine, '{}_margin'.format(name)) else: ans[idx] = m convert('left', 0, False), convert('top', 1), convert('right', 2, False), convert('bottom', 3) return ans def do_paged_render(self): if self.paged_js is None: import uuid from calibre.utils.resources import compiled_coffeescript as cc self.paged_js = cc('ebooks.oeb.display.utils') self.paged_js += cc('ebooks.oeb.display.indexing') self.paged_js += cc('ebooks.oeb.display.paged') self.paged_js += cc('ebooks.oeb.display.mathjax') if self.opts.pdf_hyphenate: self.paged_js += P('viewer/hyphenate/Hyphenator.js', data=True).decode('utf-8') hjs, self.hyphenate_lang = load_hyphenator_dicts({}, self.book_language) self.paged_js += hjs self.hf_uuid = str(uuid.uuid4()).replace('-', '') self.view.page().mainFrame().addToJavaScriptWindowObject("py_bridge", self) self.view.page().longjs_counter = 0 evaljs = self.view.page().mainFrame().evaluateJavaScript evaljs(self.paged_js) self.load_mathjax() if self.opts.pdf_hyphenate: self.hyphenate(evaljs) margin_top, margin_bottom = self.margin_top, self.margin_bottom page_margins = None if self.opts.pdf_use_document_margins: doc_margins = evaljs('document.documentElement.getAttribute("data-calibre-pdf-output-page-margins")') try: doc_margins = json.loads(doc_margins) except Exception: doc_margins = None if doc_margins and isinstance(doc_margins, dict): doc_margins = {k:float(v) for k, v in doc_margins.iteritems() if isinstance(v, (float, int)) and k in {'right', 'top', 'left', 'bottom'}} if doc_margins: margin_top = margin_bottom = 0 page_margins = self.convert_page_margins(doc_margins) amap = json.loads(evaljs(''' document.body.style.backgroundColor = "white"; // Qt WebKit cannot handle opacity with the Pdf backend s = document.createElement('style'); s.textContent = '* {opacity: 1 !important}'; document.documentElement.appendChild(s); paged_display.set_geometry(1, %d, %d, %d); paged_display.layout(); paged_display.fit_images(); ret = book_indexing.all_links_and_anchors(); window.scrollTo(0, 0); // This is needed as getting anchor positions could have caused the viewport to scroll JSON.stringify(ret); '''%(margin_top, 0, margin_bottom))) if not isinstance(amap, dict): amap = {'links':[], 'anchors':{}} # Some javascript error occurred for val in amap['anchors'].itervalues(): if isinstance(val, dict) and 'column' in val: val['column'] = int(val['column']) for href, val in amap['links']: if isinstance(val, dict) and 'column' in val: val['column'] = int(val['column']) sections = self.get_sections(amap['anchors']) tl_sections = self.get_sections(amap['anchors'], True) col = 0 if self.header: evaljs('paged_display.header_template = ' + json.dumps(self.header)) if self.footer: evaljs('paged_display.footer_template = ' + json.dumps(self.footer)) if self.header or self.footer: evaljs('paged_display.create_header_footer("%s");'%self.hf_uuid) start_page = self.current_page_num mf = self.view.page().mainFrame() def set_section(col, sections, attr): # If this page has no section, use the section from the previous page idx = col if col in sections else col - 1 if col - 1 in sections else None if idx is not None: setattr(self, attr, sections[idx][0]) from calibre.ebooks.pdf.render.toc import calculate_page_number while True: set_section(col, sections, 'current_section') set_section(col, tl_sections, 'current_tl_section') self.doc.init_page(page_margins) num = calculate_page_number(self.current_page_num, self.opts.pdf_page_number_map, evaljs) if self.header or self.footer: if evaljs('paged_display.update_header_footer(%d)'%num) is True: self.load_header_footer_images() self.painter.save() mf.render(self.painter, mf.ContentsLayer) self.painter.restore() try: nsl = int(evaljs('paged_display.next_screen_location()')) except (TypeError, ValueError): break self.doc.end_page(nsl <= 0) if nsl <= 0: break evaljs('window.scrollTo(%d, 0); paged_display.position_header_footer();'%nsl) if self.doc.errors_occurred: break col += 1 if not self.doc.errors_occurred and self.doc.current_page_num > 1: self.doc.add_links(self.current_item, start_page, amap['links'], amap['anchors'])
class MapTip(QDockWidget): closed = pyqtSignal() def __init__(self, iface: QgisInterface, html: str, point: QgsPointXY): super().__init__() self.map_canvas = iface.mapCanvas() self.point = point self.web_view = QWebView(self) self.dbg_info('map position: {}'.format(point.asWkt())) self.web_view.page().setLinkDelegationPolicy( QWebPage.DelegateAllLinks) # Handle link clicks by yourself self.web_view.setContextMenuPolicy( Qt.NoContextMenu ) # No context menu is allowed if you don't need it self.web_view.linkClicked.connect(self.on_link_clicked) self.web_view.page().settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) self.web_view.page().settings().setAttribute( QWebSettings.JavascriptEnabled, True) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setWidget(self.web_view) # assure the map tip is never larger than half the map canvas max_width = int(self.map_canvas.geometry().width() / 1.8) max_height = int(self.map_canvas.geometry().height() / 1.8) self.dbg_info('max size {} {}'.format(max_height, max_width)) self.setMaximumSize(max_width, max_height) # start with 0 size, # the content will automatically make it grow up to MaximumSize self.resize(300, 200) background_color = self.palette().base().color() background_color.setAlpha(235) stroke_color = self.palette().shadow().color() #self.setStyleSheet(".QDocWidget{{ border: 1px solid {stroke}; background-color: {bg} }}" # .format(stroke=stroke_color.name(QColor.HexArgb), # bg=background_color.name(QColor.HexArgb))) palette = self.web_view.palette() palette.setBrush(QPalette.Base, Qt.transparent) palette.setBrush(QPalette.Base, background_color) self.web_view.page().setPalette(palette) self.web_view.setAttribute(Qt.WA_OpaquePaintEvent, False) body_style = "background-color: {bg}; margin: 0".format( bg=background_color) container_style = "display: inline-block; margin: 0px" body_html = "<html><body style='{body_style}'>" \ "<div id='QgsWebViewContainer' style='{container_style}'>{html}</div><" \ "/body></html>".format(body_style=body_style, container_style=container_style, html=html) self.web_view.setHtml(body_html) scrollbar_width = self.web_view.page().mainFrame().scrollBarGeometry( Qt.Vertical).width() scrollbar_height = self.web_view.page().mainFrame().scrollBarGeometry( Qt.Horizontal).height() if scrollbar_width > 0 or scrollbar_height > 0: # Get the content size container = self.web_view.page().mainFrame().findFirstElement( "#QgsWebViewContainer") width = container.geometry().width() + 25 + scrollbar_width height = container.geometry().height() + 25 + scrollbar_height #self.resize(width, height) iface.addDockWidget(Qt.RightDockWidgetArea, self) self.setFeatures(QDockWidget.AllDockWidgetFeatures) self.setFloating(True) self.setWindowOpacity(0.9) self.move_to_point() def move_to_point(self): pixel_position = self.map_canvas.mapSettings().mapToPixel().transform( self.point) pixel_position = self.map_canvas.mapToGlobal( QPoint(pixel_position.x(), pixel_position.y())) self.move(pixel_position.x() + 10, pixel_position.y() + 10) def on_link_clicked(self, url): QDesktopServices.openUrl(url) def closeEvent(self, event: QCloseEvent): self.closed.emit() def info(self, msg="", level=Qgis.Info): QgsMessageLog.logMessage('{} {}'.format(self.__class__.__name__, msg), 'Locator bar', level) def dbg_info(self, msg=""): if DEBUG: self.info(msg)
class PDFWriter(QObject): @pyqtSlot(result=unicode_type) def title(self): return self.doc_title @pyqtSlot(result=unicode_type) def author(self): return self.doc_author @pyqtSlot(result=unicode_type) def section(self): return self.current_section @pyqtSlot(result=unicode_type) def tl_section(self): return self.current_tl_section def __init__(self, opts, log, cover_data=None, toc=None): from calibre.gui2 import must_use_qt must_use_qt() QObject.__init__(self) self.logger = self.log = log self.mathjax_dir = P('mathjax', allow_user_override=False) current_log(log) self.opts = opts self.cover_data = cover_data self.paged_js = None self.toc = toc self.loop = QEventLoop() self.view = QWebView() self.page = Page(opts, self.log) self.view.setPage(self.page) self.view.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform) self.view.loadFinished.connect(self.render_html, type=Qt.QueuedConnection) self.view.loadProgress.connect(self.load_progress) self.ignore_failure = None self.hang_check_timer = t = QTimer(self) t.timeout.connect(self.hang_check) t.setInterval(1000) for x in (Qt.Horizontal, Qt.Vertical): self.view.page().mainFrame().setScrollBarPolicy( x, Qt.ScrollBarAlwaysOff) self.report_progress = lambda x, y: x self.current_section = '' self.current_tl_section = '' def dump(self, items, out_stream, pdf_metadata): opts = self.opts page_size = get_page_size(self.opts) xdpi, ydpi = self.view.logicalDpiX(), self.view.logicalDpiY() def margin(which): val = getattr(opts, 'pdf_page_margin_' + which) if val == 0.0: val = getattr(opts, 'margin_' + which) return val ml, mr, mt, mb = map(margin, 'left right top bottom'.split()) # We cannot set the side margins in the webview as there is no right # margin for the last page (the margins are implemented with # -webkit-column-gap) self.doc = PdfDevice(out_stream, page_size=page_size, left_margin=ml, top_margin=0, right_margin=mr, bottom_margin=0, xdpi=xdpi, ydpi=ydpi, errors=self.log.error, debug=self.log.debug, compress=not opts.uncompressed_pdf, opts=opts, mark_links=opts.pdf_mark_links, page_margins=(ml, mr, mt, mb)) self.footer = opts.pdf_footer_template if self.footer: self.footer = self.footer.strip() if not self.footer and opts.pdf_page_numbers: self.footer = '<p style="text-align:center; text-indent: 0">_PAGENUM_</p>' self.header = opts.pdf_header_template if self.header: self.header = self.header.strip() min_margin = 1.5 * opts._final_base_font_size if self.footer and mb < min_margin: self.log.warn( 'Bottom margin is too small for footer, increasing it to %.1fpts' % min_margin) mb = min_margin if self.header and mt < min_margin: self.log.warn( 'Top margin is too small for header, increasing it to %.1fpts' % min_margin) mt = min_margin self.page.setViewportSize(QSize(self.doc.width(), self.doc.height())) self.render_queue = items self.total_items = len(items) mt, mb = map(self.doc.to_px, (mt, mb)) self.margin_top, self.margin_bottom = map(lambda x: int(floor(x)), (mt, mb)) self.painter = QPainter(self.doc) try: self.book_language = pdf_metadata.mi.languages[0] except Exception: self.book_language = 'eng' self.doc.set_metadata(title=pdf_metadata.title, author=pdf_metadata.author, tags=pdf_metadata.tags, mi=pdf_metadata.mi) self.doc_title = pdf_metadata.title self.doc_author = pdf_metadata.author self.painter.save() try: if self.cover_data is not None: p = QPixmap() try: p.loadFromData(self.cover_data) except TypeError: self.log.warn( 'This ebook does not have a raster cover, cannot generate cover for PDF' '. Cover type: %s' % type(self.cover_data)) if not p.isNull(): self.doc.init_page() draw_image_page(QRect(*self.doc.full_page_rect), self.painter, p, preserve_aspect_ratio=self.opts. preserve_cover_aspect_ratio) self.doc.end_page() finally: self.painter.restore() QTimer.singleShot(0, self.render_book) if self.loop.exec_() == 1: raise Exception('PDF Output failed, see log for details') if self.toc is not None and len(self.toc) > 0: self.doc.add_outline(self.toc) self.painter.end() if self.doc.errors_occurred: raise Exception('PDF Output failed, see log for details') def render_inline_toc(self): evaljs = self.view.page().mainFrame().evaluateJavaScript self.rendered_inline_toc = True from calibre.ebooks.pdf.render.toc import toc_as_html raw = toc_as_html(self.toc, self.doc, self.opts, evaljs) pt = PersistentTemporaryFile('_pdf_itoc.htm') pt.write(raw) pt.close() self.render_queue.append(pt.name) self.render_next() def render_book(self): if self.doc.errors_occurred: return self.loop.exit(1) try: if not self.render_queue: if self.opts.pdf_add_toc and self.toc is not None and len( self.toc) > 0 and not hasattr(self, 'rendered_inline_toc'): return self.render_inline_toc() self.loop.exit() else: self.render_next() except: self.logger.exception('Rendering failed') self.loop.exit(1) def render_next(self): item = unicode_type(self.render_queue.pop(0)) self.logger.debug('Processing %s...' % item) self.current_item = item load_html(item, self.view) self.last_load_progress_at = monotonic() self.hang_check_timer.start() def load_progress(self, progress): self.last_load_progress_at = monotonic() def hang_check(self): if monotonic() - self.last_load_progress_at > 60: self.log.warn('Timed out waiting for %s to render' % self.current_item) self.ignore_failure = self.current_item self.view.stop() def render_html(self, ok): self.hang_check_timer.stop() if self.ignore_failure == self.current_item: ok = True self.ignore_failure = None if ok: try: self.do_paged_render() except: self.log.exception('Rendering failed') self.loop.exit(1) return else: # The document is so corrupt that we can't render the page. self.logger.error('Document %s cannot be rendered.' % self.current_item) self.loop.exit(1) return done = self.total_items - len(self.render_queue) self.report_progress( done / self.total_items, _('Rendered %s' % os.path.basename(self.current_item))) self.render_book() @property def current_page_num(self): return self.doc.current_page_num def load_mathjax(self): evaljs = self.view.page().mainFrame().evaluateJavaScript mjpath = self.mathjax_dir.replace(os.sep, '/') if iswindows: mjpath = u'/' + mjpath if bool( evaljs(''' window.mathjax.base = %s; mathjax.check_for_math(); mathjax.math_present ''' % (json.dumps(mjpath, ensure_ascii=False)))): self.log.debug('Math present, loading MathJax') while not bool(evaljs('mathjax.math_loaded')): self.loop.processEvents(self.loop.ExcludeUserInputEvents) # give the MathJax fonts time to load for i in range(5): self.loop.processEvents(self.loop.ExcludeUserInputEvents) evaljs( 'document.getElementById("MathJax_Message").style.display="none";' ) def load_header_footer_images(self): from calibre.utils.monotonic import monotonic evaljs = self.view.page().mainFrame().evaluateJavaScript st = monotonic() while not evaljs('paged_display.header_footer_images_loaded()'): self.loop.processEvents(self.loop.ExcludeUserInputEvents) if monotonic() - st > 5: self.log.warn( 'Header and footer images have not loaded in 5 seconds, ignoring' ) break def get_sections(self, anchor_map, only_top_level=False): sections = defaultdict(list) ci = os.path.abspath(os.path.normcase(self.current_item)) if self.toc is not None: tocentries = self.toc.top_level_items( ) if only_top_level else self.toc.flat() for toc in tocentries: path = toc.abspath or None frag = toc.fragment or None if path is None: continue path = os.path.abspath(os.path.normcase(path)) if path == ci: col = 0 if frag and frag in anchor_map: col = anchor_map[frag]['column'] sections[col].append(toc.text or _('Untitled')) return sections def hyphenate(self, evaljs): evaljs(u'''\ Hyphenator.config( { 'minwordlength' : 6, // 'hyphenchar' : '|', 'displaytogglebox' : false, 'remoteloading' : false, 'doframes' : true, 'defaultlanguage' : 'en', 'storagetype' : 'session', 'onerrorhandler' : function (e) { console.log(e); } }); Hyphenator.hyphenate(document.body, "%s"); ''' % self.hyphenate_lang) def convert_page_margins(self, doc_margins): ans = [0, 0, 0, 0] def convert(name, idx, vertical=True): m = doc_margins.get(name) if m is None: ans[idx] = getattr(self.doc.engine, '{}_margin'.format(name)) else: ans[idx] = m convert('left', 0, False), convert('top', 1), convert('right', 2, False), convert('bottom', 3) return ans def do_paged_render(self): if self.paged_js is None: import uuid from calibre.utils.resources import compiled_coffeescript as cc self.paged_js = cc('ebooks.oeb.display.utils').decode('utf-8') self.paged_js += cc('ebooks.oeb.display.indexing').decode('utf-8') self.paged_js += cc('ebooks.oeb.display.paged').decode('utf-8') self.paged_js += cc('ebooks.oeb.display.mathjax').decode('utf-8') if self.opts.pdf_hyphenate: self.paged_js += P('viewer/hyphenate/Hyphenator.js', data=True).decode('utf-8') hjs, self.hyphenate_lang = load_hyphenator_dicts( {}, self.book_language) self.paged_js += hjs self.hf_uuid = str(uuid.uuid4()).replace('-', '') self.view.page().mainFrame().addToJavaScriptWindowObject( "py_bridge", self) self.view.page().longjs_counter = 0 evaljs = self.view.page().mainFrame().evaluateJavaScript evaljs(self.paged_js) self.load_mathjax() if self.opts.pdf_hyphenate: self.hyphenate(evaljs) margin_top, margin_bottom = self.margin_top, self.margin_bottom page_margins = None if self.opts.pdf_use_document_margins: doc_margins = evaljs( 'document.documentElement.getAttribute("data-calibre-pdf-output-page-margins")' ) try: doc_margins = json.loads(doc_margins) except Exception: doc_margins = None if doc_margins and isinstance(doc_margins, dict): doc_margins = { k: float(v) for k, v in iteritems(doc_margins) if isinstance(v, numbers.Number) and k in {'right', 'top', 'left', 'bottom'} } if doc_margins: margin_top = margin_bottom = 0 page_margins = self.convert_page_margins(doc_margins) amap = json.loads( evaljs(''' document.body.style.backgroundColor = "white"; // Qt WebKit cannot handle opacity with the Pdf backend s = document.createElement('style'); s.textContent = '* {opacity: 1 !important}'; document.documentElement.appendChild(s); paged_display.set_geometry(1, %d, %d, %d); paged_display.layout(); paged_display.fit_images(); ret = book_indexing.all_links_and_anchors(); window.scrollTo(0, 0); // This is needed as getting anchor positions could have caused the viewport to scroll JSON.stringify(ret); ''' % (margin_top, 0, margin_bottom))) if not isinstance(amap, dict): amap = { 'links': [], 'anchors': {} } # Some javascript error occurred for val in itervalues(amap['anchors']): if isinstance(val, dict) and 'column' in val: val['column'] = int(val['column']) for href, val in amap['links']: if isinstance(val, dict) and 'column' in val: val['column'] = int(val['column']) sections = self.get_sections(amap['anchors']) tl_sections = self.get_sections(amap['anchors'], True) col = 0 if self.header: evaljs('paged_display.header_template = ' + json.dumps(self.header)) if self.footer: evaljs('paged_display.footer_template = ' + json.dumps(self.footer)) if self.header or self.footer: evaljs('paged_display.create_header_footer("%s");' % self.hf_uuid) start_page = self.current_page_num mf = self.view.page().mainFrame() def set_section(col, sections, attr): # If this page has no section, use the section from the previous page idx = col if col in sections else col - 1 if col - 1 in sections else None if idx is not None: setattr(self, attr, sections[idx][0]) from calibre.ebooks.pdf.render.toc import calculate_page_number while True: set_section(col, sections, 'current_section') set_section(col, tl_sections, 'current_tl_section') self.doc.init_page(page_margins) num = calculate_page_number(self.current_page_num, self.opts.pdf_page_number_map, evaljs) if self.header or self.footer: if evaljs('paged_display.update_header_footer(%d)' % num) is True: self.load_header_footer_images() self.painter.save() mf.render(self.painter, mf.ContentsLayer) self.painter.restore() try: nsl = int(evaljs('paged_display.next_screen_location()')) except (TypeError, ValueError): break self.doc.end_page(nsl <= 0) if nsl <= 0: break evaljs( 'window.scrollTo(%d, 0); paged_display.position_header_footer();' % nsl) if self.doc.errors_occurred: break col += 1 if not self.doc.errors_occurred and self.doc.current_page_num > 1: self.doc.add_links(self.current_item, start_page, amap['links'], amap['anchors'])
class DrawLotteryView(QObject): def __init__(self, parent): super(DrawLotteryView, self).__init__() self.pwindow = parent #获取父窗口指针 self.states = QStateMachine() sinitinfo = QState() sltypeinfo = QState() sprizeinfo = QState() svnumberwindow = QState() sdrawlottery = QState() sfinal = QState() sinitinfo.addTransition(self.on_nextstep_event, sltypeinfo) sltypeinfo.addTransition(self.on_nextstep_event, sprizeinfo) sprizeinfo.addTransition(self.on_nextstep_event, svnumberwindow) svnumberwindow.addTransition(self.on_nextstep_event, sdrawlottery) sdrawlottery.addTransition(self.on_nextstep_event, sfinal) sfinal.addTransition(self.on_final_event, sinitinfo) sinitinfo.entered.connect(self.initinfo) sltypeinfo.entered.connect(self.viewltypeinfo) sprizeinfo.entered.connect(self.viewprizeinfo) svnumberwindow.entered.connect(self.viewnumberwindow) sdrawlottery.entered.connect(self.drawlottery) sfinal.entered.connect(self.final) self.states.addState(sinitinfo) self.states.addState(sltypeinfo) self.states.addState(sprizeinfo) self.states.addState(svnumberwindow) self.states.addState(sdrawlottery) self.states.addState(sfinal) self.states.setInitialState(sinitinfo) self.states.start() def show(self): #It is IMPERATIVE that all forward slashes are scrubbed out, otherwise QTWebKit seems to be # easily confused # kickOffHTML = 'file:///' + join(dirname(__file__).replace('\\', '/'), "www/test02.html").replace('\\', '/') # kickOffHTML = 'file:///' + join(dirname(__file__).replace('\\', '/'), "www/test02.html").replace('\\', '/') kickOffHTML = 'file:///' + QDir().absolutePath() + SysConfig().getlotterywindowspath() + 'index.html' # kickOffHTML = 'http://get.webgl.org/' # kickOffHTML = 'http://www.airtightinteractive.com/demos/js/nebula/' #This is basically a browser instance # self.gweb = QGraphicsWebView() self.web = QWebView() self.web.setMinimumSize(1024,680) self.web.setWindowFlags(Qt.FramelessWindowHint) #无边框 self.web.setContextMenuPolicy(0) #关闭右键 #Unlikely to matter but prefer to be waiting for callback then try to catch # it in time. self.web.loadFinished.connect(self.onLoad) self.web.load(QUrl(kickOffHTML)) self.web.show() # self.scene = QGraphicsScene() # self.scene.addItem(self.gweb) # self.view = QGraphicsView() # self.view.setScene(self.scene) # self.view.show() def onLoad(self): #如果mywebinterface未初始化,则初始化mywebinterface # if getattr(self, "mywebinterface", False) == False: # self.mywebinterface = WebInterface() #This is the body of a web browser tab self.myPage = self.web.page() self.myPage.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) self.myPage.settings().setAttribute(QWebSettings.JavascriptEnabled, True) self.myPage.settings().setAttribute(QWebSettings.WebGLEnabled, True) self.myPage.settings().setAttribute(QWebSettings.AcceleratedCompositingEnabled, True) #This is the actual context/frame a webpage is running in. # Other frames could include iframes or such. self.myFrame = self.myPage.mainFrame() # ATTENTION here's the magic that sets a bridge between Python to HTML self.myFrame.addToJavaScriptWindowObject("mywebinterface", self) #Tell the HTML side, we are open for business self.myFrame.evaluateJavaScript("ApplicationIsReady()") def initinfo(self): # print('initinfo') cltypes = self.pwindow.getcurrentqueue() if cltypes == None: self.isfinished = True else: self.isfinished = False self.currentltype = cltypes['ltype'] self.currentnumber = cltypes['number'] #当前奖项需要抽取的数量 self.drawcount = int(self.currentnumber) #当前抽奖序列的人数,需要重复抽奖的次数 self.isdrawing = False #当前是否为抽奖状态 @pyqtSlot() def nextstep(self): if self.isfinished: # print('close this window') self.on_message_event.emit('当前抽奖序列已抽取完毕!<br>按退出按钮退出!') else: if self.isdrawing and self.drawcount > 0: self.drawlottery() else: # print('nextstep') self.on_nextstep_event.emit() @pyqtSlot() def exitwindow(self): # print('exit window') self.web.close() def viewltypeinfo(self): # print('viewltypeinfo') self.on_viewltypeinfo_event.emit(self.currentltype, int(self.currentnumber)) def viewprizeinfo(self): # print('return prizeinfo') ls = SysConfig().getprizeitempicandnote(self.currentltype) icon = ls[0] notes = ls[1] self.on_viewprizeinfo_event.emit(icon, notes) def viewnumberwindow(self): # print('return viewnumberwindow') self.on_viewnumberwindow_event.emit() def drawlottery(self): # print('return drawlottery') progress = str(int(self.currentnumber)-self.drawcount+1) + '/' + self.currentnumber self.on_viewprogress_event.emit(progress) try: dl = DAllList().getwininfo() sid = dl[0][2] + dl[0][5] + dl[0][6] + dl[0][7] name = dl[1] DAllList().setwininfo(self.currentltype,dl[0]) except: sid = '0000' name = 'error, retry' self.drawcount = 0 self.isdrawing = False self.on_drawlottery_event.emit(sid, name) pass self.on_drawlottery_event.emit(sid, name) self.drawcount -= 1 if self.drawcount == 0: self.isdrawing = False else: self.isdrawing = True def final(self): print('final') self.on_final_event.emit() @pyqtSlot() def changesize(self): # print('changesize') if self.web.isFullScreen(): self.web.showNormal() else: self.web.showFullScreen() on_nextstep_event = pyqtSignal() on_viewltypeinfo_event = pyqtSignal(str, int) on_viewprizeinfo_event = pyqtSignal(str, str) on_viewluckguyinfo_event = pyqtSignal(str, str) on_viewnumberwindow_event = pyqtSignal() on_drawlottery_event = pyqtSignal(str, str) on_final_event = pyqtSignal() on_message_event = pyqtSignal(str) on_viewprogress_event = pyqtSignal(str) # app = QApplication(sys.argv) # # myWebApp = DrawLotteryView() # myWebApp.show() # # exit(app.exec_())
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = QWebView(self) self.details.sizeHint = self.details_size_hint self.details.page().setLinkDelegationPolicy(self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked(gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip(_('Next [%s]')% unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip(_('Previous [%s]')% unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width/2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def link_clicked(self, qurl): link = unicode(qurl.toString(QUrl.None)) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.model().new_bookdisplay_data.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) if self.gui.cover_flow: self.gui.cover_flow.dataChanged() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): self.refresh(mi.row_number, mi) def move(self, delta=1): idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): if self.view.isVisible(): self.view.scrollTo(ni) self.view.setCurrentIndex(ni) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image(pixmap.width(), pixmap.height(), self.cover.size().width()-10, self.cover.size().height()-10) if scaled: pixmap = pixmap.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in {True, 'true'} else _( 'This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d')%dict(width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex())-1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) self.resize_cover() html = render_html(mi, self.css, True, self, all_fields=True) self.details.setHtml(html) self.marked = mi.marked self.cover.setBackgroundBrush(self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()
class PreviewerHTML(QWidget): """ Class implementing a previewer widget for HTML, Markdown and ReST files. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(PreviewerHTML, self).__init__(parent) self.__layout = QVBoxLayout(self) self.titleLabel = QLabel(self) self.titleLabel.setWordWrap(True) self.titleLabel.setTextInteractionFlags(Qt.NoTextInteraction) self.__layout.addWidget(self.titleLabel) try: from PyQt5.QtWebEngineWidgets import QWebEngineView self.previewView = QWebEngineView(self) self.__usesWebKit = False except ImportError: from PyQt5.QtWebKitWidgets import QWebPage, QWebView self.previewView = QWebView(self) self.previewView.page().setLinkDelegationPolicy( QWebPage.DelegateAllLinks) self.__usesWebKit = True sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.previewView.sizePolicy().hasHeightForWidth()) self.previewView.setSizePolicy(sizePolicy) self.previewView.setContextMenuPolicy(Qt.NoContextMenu) self.previewView.setUrl(QUrl("about:blank")) self.__layout.addWidget(self.previewView) self.jsCheckBox = QCheckBox(self.tr("Enable JavaScript"), self) self.jsCheckBox.setToolTip(self.tr( "Select to enable JavaScript for HTML previews")) self.__layout.addWidget(self.jsCheckBox) self.ssiCheckBox = QCheckBox(self.tr("Enable Server Side Includes"), self) self.ssiCheckBox.setToolTip(self.tr( "Select to enable support for Server Side Includes")) self.__layout.addWidget(self.ssiCheckBox) self.jsCheckBox.clicked[bool].connect(self.on_jsCheckBox_clicked) self.ssiCheckBox.clicked[bool].connect(self.on_ssiCheckBox_clicked) self.previewView.titleChanged.connect(self.on_previewView_titleChanged) if self.__usesWebKit: self.previewView.linkClicked.connect( self.on_previewView_linkClicked) self.jsCheckBox.setChecked( Preferences.getUI("ShowFilePreviewJS")) self.ssiCheckBox.setChecked( Preferences.getUI("ShowFilePreviewSSI")) self.__scrollBarPositions = {} self.__vScrollBarAtEnd = {} self.__hScrollBarAtEnd = {} self.__processingThread = PreviewProcessingThread() self.__processingThread.htmlReady.connect(self.__setHtml) self.__previewedPath = None self.__previewedEditor = None def shutdown(self): """ Public method to perform shutdown actions. """ self.__processingThread.wait() @pyqtSlot(bool) def on_jsCheckBox_clicked(self, checked): """ Private slot to enable/disable JavaScript. @param checked state of the checkbox (boolean) """ Preferences.setUI("ShowFilePreviewJS", checked) self.__setJavaScriptEnabled(checked) def __setJavaScriptEnabled(self, enable): """ Private method to enable/disable JavaScript. @param enable flag indicating the enable state (boolean) """ self.jsCheckBox.setChecked(enable) settings = self.previewView.settings() settings.setAttribute(settings.JavascriptEnabled, enable) self.processEditor() @pyqtSlot(bool) def on_ssiCheckBox_clicked(self, checked): """ Private slot to enable/disable SSI. @param checked state of the checkbox (boolean) """ Preferences.setUI("ShowFilePreviewSSI", checked) self.processEditor() def processEditor(self, editor=None): """ Public slot to process an editor's text. @param editor editor to be processed (Editor) """ if editor is None: editor = self.__previewedEditor else: self.__previewedEditor = editor if editor is not None: fn = editor.getFileName() if fn: extension = os.path.normcase(os.path.splitext(fn)[1][1:]) else: extension = "" if extension in \ Preferences.getEditor("PreviewHtmlFileNameExtensions") or \ editor.getLanguage() == "HTML": language = "HTML" elif extension in \ Preferences.getEditor("PreviewMarkdownFileNameExtensions"): language = "Markdown" elif extension in \ Preferences.getEditor("PreviewRestFileNameExtensions"): language = "ReST" else: self.__setHtml(fn, self.tr( "<p>No preview available for this type of file.</p>")) return if fn: project = e5App().getObject("Project") if project.isProjectFile(fn): rootPath = project.getProjectPath() else: rootPath = os.path.dirname(os.path.abspath(fn)) else: rootPath = "" self.__processingThread.process( fn, language, editor.text(), self.ssiCheckBox.isChecked(), rootPath, Preferences.getEditor("PreviewRestUseSphinx")) def __setHtml(self, filePath, html): """ Private method to set the HTML to the view and restore the scroll bars positions. @param filePath file path of the previewed editor (string) @param html processed HTML text ready to be shown (string) """ self.__previewedPath = Utilities.normcasepath( Utilities.fromNativeSeparators(filePath)) self.__saveScrollBarPositions() if self.__usesWebKit: self.previewView.page().mainFrame().contentsSizeChanged.connect( self.__restoreScrollBarPositions) else: self.previewView.page().loadFinished.connect( self.__restoreScrollBarPositions) self.previewView.setHtml(html, baseUrl=QUrl.fromLocalFile(filePath)) @pyqtSlot(str) def on_previewView_titleChanged(self, title): """ Private slot to handle a change of the title. @param title new title (string) """ if title: self.titleLabel.setText(self.tr("Preview - {0}").format(title)) else: self.titleLabel.setText(self.tr("Preview")) def __saveScrollBarPositions(self): """ Private method to save scroll bar positions for a previewed editor. """ if self.__usesWebKit: frame = self.previewView.page().mainFrame() if frame.contentsSize() == QSize(0, 0): return # no valid data, nothing to save pos = frame.scrollPosition() self.__scrollBarPositions[self.__previewedPath] = pos self.__hScrollBarAtEnd[self.__previewedPath] = \ frame.scrollBarMaximum(Qt.Horizontal) == pos.x() self.__vScrollBarAtEnd[self.__previewedPath] = \ frame.scrollBarMaximum(Qt.Vertical) == pos.y() else: from PyQt5.QtCore import QPoint pos = self.__execJavaScript( "(function() {" "var res = {" " x: 0," " y: 0," "};" "res.x = window.scrollX;" "res.y = window.scrollY;" "return res;" "})()" ) pos = QPoint(pos["x"], pos["y"]) self.__scrollBarPositions[self.__previewedPath] = pos self.__hScrollBarAtEnd[self.__previewedPath] = False self.__vScrollBarAtEnd[self.__previewedPath] = False def __restoreScrollBarPositions(self): """ Private method to restore scroll bar positions for a previewed editor. """ if self.__usesWebKit: try: self.previewView.page().mainFrame().contentsSizeChanged.\ disconnect(self.__restoreScrollBarPositions) except TypeError: # not connected, simply ignore it pass if self.__previewedPath not in self.__scrollBarPositions: return frame = self.previewView.page().mainFrame() frame.setScrollPosition( self.__scrollBarPositions[self.__previewedPath]) if self.__hScrollBarAtEnd[self.__previewedPath]: frame.setScrollBarValue( Qt.Horizontal, frame.scrollBarMaximum(Qt.Horizontal)) if self.__vScrollBarAtEnd[self.__previewedPath]: frame.setScrollBarValue( Qt.Vertical, frame.scrollBarMaximum(Qt.Vertical)) else: if self.__previewedPath not in self.__scrollBarPositions: return pos = self.__scrollBarPositions[self.__previewedPath] self.previewView.page().runJavaScript( "window.scrollTo({0}, {1});".format(pos.x(), pos.y())) @pyqtSlot(QUrl) def on_previewView_linkClicked(self, url): """ Private slot handling the clicking of a link. @param url url of the clicked link (QUrl) """ e5App().getObject("UserInterface").launchHelpViewer(url.toString()) def __execJavaScript(self, script): """ Private function to execute a JavaScript function Synchroneously. @param script JavaScript script source to be executed @type str @return result of the script @rtype depending upon script result """ from PyQt5.QtCore import QEventLoop loop = QEventLoop() resultDict = {"res": None} def resultCallback(res, resDict=resultDict): if loop and loop.isRunning(): resDict["res"] = res loop.quit() self.previewView.page().runJavaScript( script, resultCallback) loop.exec_() return resultDict["res"]
class Printing(QObject): def __init__(self, iterator, parent): QObject.__init__(self, parent) self.current_index = 0 self.iterator = iterator self.view = QWebView(self.parent()) self.mf = mf = self.view.page().mainFrame() for x in (Qt.Horizontal, Qt.Vertical): mf.setScrollBarPolicy(x, Qt.ScrollBarAlwaysOff) self.view.loadFinished.connect(self.load_finished) self.paged_js = compiled_coffeescript('ebooks.oeb.display.utils') self.paged_js += compiled_coffeescript('ebooks.oeb.display.paged') def load_finished(self, ok): self.loaded_ok = ok def start_print(self): self.pd = QPrintDialog(self.parent()) self.pd.open(self._start_print) def _start_print(self): self.do_print(self.pd.printer()) def start_preview(self): self.pd = QPrintPreviewDialog(self.parent()) self.pd.paintRequested.connect(self.do_print) self.pd.exec_() def do_print(self, printer): painter = QPainter(printer) zoomx = printer.logicalDpiX() / self.view.logicalDpiX() zoomy = printer.logicalDpiY() / self.view.logicalDpiY() painter.scale(zoomx, zoomy) pr = printer.pageRect() self.view.page().setViewportSize( QSize(pr.width() / zoomx, pr.height() / zoomy)) evaljs = self.mf.evaluateJavaScript loop = QEventLoop(self) pagenum = 0 from_, to = printer.fromPage(), printer.toPage() first = True for path in self.iterator.spine: self.loaded_ok = None load_html(path, self.view, codec=getattr(path, 'encoding', 'utf-8'), mime_type=getattr(path, 'mime_type', None)) while self.loaded_ok is None: loop.processEvents(loop.ExcludeUserInputEvents) if not self.loaded_ok: return error_dialog(self.parent(), _('Failed to render'), _('Failed to render document %s') % path, show=True) evaljs(self.paged_js) evaljs(''' document.body.style.backgroundColor = "white"; paged_display.set_geometry(1, 0, 0, 0); paged_display.layout(); paged_display.fit_images(); ''') while True: pagenum += 1 if (pagenum >= from_ and (to == 0 or pagenum <= to)): if not first: printer.newPage() first = False self.mf.render(painter) try: nsl = int(evaljs('paged_display.next_screen_location()')) except (TypeError, ValueError): break if nsl <= 0: break evaljs('window.scrollTo(%d, 0)' % nsl) painter.end()
class MainWindow(QMainWindow): def __init__(self, url): super(MainWindow, self).__init__() self.progress = 0 fd = QFile(":/jquery.min.js") if fd.open(QIODevice.ReadOnly | QFile.Text): self.jQuery = QTextStream(fd).readAll() fd.close() else: self.jQuery = '' QNetworkProxyFactory.setUseSystemConfiguration(True) self.view = QWebView(self) self.view.load(url) self.view.loadFinished.connect(self.adjustLocation) self.view.titleChanged.connect(self.adjustTitle) self.view.loadProgress.connect(self.setProgress) self.view.loadFinished.connect(self.finishLoading) self.locationEdit = QLineEdit(self) self.locationEdit.setSizePolicy(QSizePolicy.Expanding, self.locationEdit.sizePolicy().verticalPolicy()) self.locationEdit.returnPressed.connect(self.changeLocation) toolBar = self.addToolBar("Navigation") toolBar.addAction(self.view.pageAction(QWebPage.Back)) toolBar.addAction(self.view.pageAction(QWebPage.Forward)) toolBar.addAction(self.view.pageAction(QWebPage.Reload)) toolBar.addAction(self.view.pageAction(QWebPage.Stop)) toolBar.addWidget(self.locationEdit) viewMenu = self.menuBar().addMenu("&View") viewSourceAction = QAction("Page Source", self) viewSourceAction.triggered.connect(self.viewSource) viewMenu.addAction(viewSourceAction) effectMenu = self.menuBar().addMenu("&Effect") effectMenu.addAction("Highlight all links", self.highlightAllLinks) self.rotateAction = QAction( self.style().standardIcon(QStyle.SP_FileDialogDetailedView), "Turn images upside down", self, checkable=True, toggled=self.rotateImages) effectMenu.addAction(self.rotateAction) toolsMenu = self.menuBar().addMenu("&Tools") toolsMenu.addAction("Remove GIF images", self.removeGifImages) toolsMenu.addAction("Remove all inline frames", self.removeInlineFrames) toolsMenu.addAction("Remove all object elements", self.removeObjectElements) toolsMenu.addAction("Remove all embedded elements", self.removeEmbeddedElements) self.setCentralWidget(self.view) def viewSource(self): accessManager = self.view.page().networkAccessManager() request = QNetworkRequest(self.view.url()) reply = accessManager.get(request) reply.finished.connect(self.slotSourceDownloaded) def slotSourceDownloaded(self): reply = self.sender() self.textEdit = QTextEdit() self.textEdit.setAttribute(Qt.WA_DeleteOnClose) self.textEdit.show() self.textEdit.setPlainText(QTextStream(reply).readAll()) self.textEdit.resize(600, 400) reply.deleteLater() def adjustLocation(self): self.locationEdit.setText(self.view.url().toString()) def changeLocation(self): url = QUrl.fromUserInput(self.locationEdit.text()) self.view.load(url) self.view.setFocus() def adjustTitle(self): if 0 < self.progress < 100: self.setWindowTitle("%s (%s%%)" % (self.view.title(), self.progress)) else: self.setWindowTitle(self.view.title()) def setProgress(self, p): self.progress = p self.adjustTitle() def finishLoading(self): self.progress = 100 self.adjustTitle() self.view.page().mainFrame().evaluateJavaScript(self.jQuery) self.rotateImages(self.rotateAction.isChecked()) def highlightAllLinks(self): code = """$('a').each( function () { $(this).css('background-color', 'yellow') } )""" self.view.page().mainFrame().evaluateJavaScript(code) def rotateImages(self, invert): if invert: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(180deg)') } )""" else: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(0deg)') } )""" self.view.page().mainFrame().evaluateJavaScript(code) def removeGifImages(self): code = "$('[src*=gif]').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeInlineFrames(self): code = "$('iframe').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeObjectElements(self): code = "$('object').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeEmbeddedElements(self): code = "$('embed').remove()" self.view.page().mainFrame().evaluateJavaScript(code)
class GPSWindow(QDialog): latitude = "" longitude = "" def __init__(self): log.info("GPSWindow.__init__") ui_gpswindow, QtBaseClass = uic.loadUiType(qtGPSUIFile) super(GPSWindow, self).__init__() self.ui = ui_gpswindow() self.ui.setupUi(self) self.setWindowTitle("Google Google Maps Maps") self.web = QWebView() self.web.setMinimumSize(800, 800) self.web.page().mainFrame().addToJavaScriptWindowObject('self', self) self.web.setHtml(maphtml) self.ui.frame.setLayout(self.ui.gridLayout) self.ui.gridLayout.addWidget(self.web, 0, 0, 1, 1) self.ui.dd_latitude.setText(self.latitude) self.ui.dd_longitude.setText(self.longitude) self.ui.dms_latitude.setText(self.latitude) self.ui.dms_longitude.setText(self.longitude) self.setStyleSheet(open("styles.css", "r").read()) @pyqtSlot(float, float) def click(self, lat, lng): log.debug("click") self.latitude = str("%.4f" % lat) self.longitude = str("%.4f" % lng) self.ui.dd_latitude.setText(self.latitude) self.ui.dd_longitude.setText(self.longitude) degrees = math.floor(lat) minutes = math.floor(60 * (lat - degrees)) seconds = 3600 * (lat - degrees) - 60 * minutes log.debug("lat=%f, minutes=%f, seconds=%f" % (lat, minutes, seconds)) self.ui.dms_latitude.setText("N %.0f %.0f' %.2f\"" % (degrees, minutes, seconds)) degrees = math.floor(lng) minutes = math.floor(60 * (lng - degrees)) seconds = 3600 * (lng - degrees) - 60 * minutes self.ui.dms_longitude.setText("E %.0f %.0f' %.2f\"" % (degrees, minutes, seconds)) def save(self): log.info("GPSWindow SAVE") self.close() return self.latitude, self.longitude def cancel(self): log.info("GPSWindow Cancel") self.close() return self.Rejected
class Browser(QWidget): """LilyPond documentation browser widget.""" def __init__(self, dockwidget): super(Browser, self).__init__(dockwidget) layout = QVBoxLayout(spacing=0) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.toolbar = tb = QToolBar() self.webview = QWebView(contextMenuPolicy=Qt.CustomContextMenu) self.chooser = QComboBox(sizeAdjustPolicy=QComboBox.AdjustToContents) self.search = SearchEntry(maximumWidth=200) layout.addWidget(self.toolbar) layout.addWidget(self.webview) ac = dockwidget.actionCollection ac.help_back.triggered.connect(self.webview.back) ac.help_forward.triggered.connect(self.webview.forward) ac.help_home.triggered.connect(self.showHomePage) ac.help_print.triggered.connect(self.slotPrint) self.webview.page().setNetworkAccessManager( lilydoc.network.accessmanager()) self.webview.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.webview.page().linkClicked.connect(self.openUrl) self.webview.page().setForwardUnsupportedContent(True) self.webview.page().unsupportedContent.connect(self.slotUnsupported) self.webview.urlChanged.connect(self.slotUrlChanged) self.webview.customContextMenuRequested.connect( self.slotShowContextMenu) tb.addAction(ac.help_back) tb.addAction(ac.help_forward) tb.addSeparator() tb.addAction(ac.help_home) tb.addAction(ac.help_print) tb.addSeparator() tb.addWidget(self.chooser) tb.addWidget(self.search) self.chooser.activated[int].connect(self.showHomePage) self.search.textEdited.connect(self.slotSearchChanged) self.search.returnPressed.connect(self.slotSearchReturnPressed) dockwidget.mainwindow().iconSizeChanged.connect( self.updateToolBarSettings) dockwidget.mainwindow().toolButtonStyleChanged.connect( self.updateToolBarSettings) app.settingsChanged.connect(self.readSettings) self.readSettings() self.loadDocumentation() self.showInitialPage() app.settingsChanged.connect(self.loadDocumentation) app.translateUI(self) def readSettings(self): s = QSettings() s.beginGroup("documentation") ws = self.webview.page().settings() family = s.value("fontfamily", self.font().family(), str) size = s.value("fontsize", 16, int) ws.setFontFamily(QWebSettings.StandardFont, family) ws.setFontSize(QWebSettings.DefaultFontSize, size) fixed = textformats.formatData('editor').font ws.setFontFamily(QWebSettings.FixedFont, fixed.family()) ws.setFontSize(QWebSettings.DefaultFixedFontSize, fixed.pointSizeF() * 96 / 72) def keyPressEvent(self, ev): if ev.text() == "/": self.search.setFocus() else: super(Browser, self).keyPressEvent(ev) def translateUI(self): try: self.search.setPlaceholderText(_("Search...")) except AttributeError: pass # not in Qt 4.6 def showInitialPage(self): """Shows the preferred start page. If a local documentation instance already has a suitable version, just loads it. Otherwise connects to the allLoaded signal, that is emitted when all the documentation instances have loaded their version information and then shows the start page (if another page wasn't yet loaded). """ if self.webview.url().isEmpty(): docs = lilydoc.manager.docs() version = lilypondinfo.preferred().version() index = -1 if version: for num, doc in enumerate(docs): if doc.version() is not None and doc.version() >= version: index = num # a suitable documentation is found break if index == -1: # nothing found (or LilyPond version not available), # wait for loading or show the most recent version if not lilydoc.manager.loaded(): lilydoc.manager.allLoaded.connect(self.showInitialPage) return index = len(docs) - 1 self.chooser.setCurrentIndex(index) self.showHomePage() def loadDocumentation(self): """Puts the available documentation instances in the combobox.""" i = self.chooser.currentIndex() self.chooser.clear() for doc in lilydoc.manager.docs(): v = doc.versionString() if doc.isLocal(): t = _("(local)") else: t = _("({hostname})").format(hostname=doc.url().host()) self.chooser.addItem("{0} {1}".format(v or _("<unknown>"), t)) self.chooser.setCurrentIndex(i) if not lilydoc.manager.loaded(): lilydoc.manager.allLoaded.connect(self.loadDocumentation, -1) return def updateToolBarSettings(self): mainwin = self.parentWidget().mainwindow() self.toolbar.setIconSize(mainwin.iconSize()) self.toolbar.setToolButtonStyle(mainwin.toolButtonStyle()) def showManual(self): """Invoked when the user presses F1.""" self.slotHomeFrescobaldi() # TEMP def slotUrlChanged(self): ac = self.parentWidget().actionCollection ac.help_back.setEnabled(self.webview.history().canGoBack()) ac.help_forward.setEnabled(self.webview.history().canGoForward()) def openUrl(self, url): if url.path().endswith(('.ily', '.lyi', '.ly')): self.sourceViewer().showReply(lilydoc.network.get(url)) else: self.webview.load(url) def slotUnsupported(self, reply): helpers.openUrl(reply.url()) def slotSearchChanged(self): text = self.search.text() if not text.startswith(':'): self.webview.page().findText(text, QWebPage.FindWrapsAroundDocument) def slotSearchReturnPressed(self): text = self.search.text() if not text.startswith(':'): self.slotSearchChanged() else: pass # TODO: implement full doc search def sourceViewer(self): try: return self._sourceviewer except AttributeError: from . import sourceviewer self._sourceviewer = sourceviewer.SourceViewer(self) return self._sourceviewer def showHomePage(self): """Shows the homepage of the LilyPond documentation.""" i = self.chooser.currentIndex() if i < 0: i = 0 doc = lilydoc.manager.docs()[i] url = doc.home() if doc.isLocal(): path = url.toLocalFile() langs = lilydoc.network.langs() if langs: for lang in langs: if os.path.exists(path + '.' + lang + '.html'): path += '.' + lang break url = QUrl.fromLocalFile(path + '.html') self.webview.load(url) def slotPrint(self): printer = QPrinter() dlg = QPrintDialog(printer, self) dlg.setWindowTitle(app.caption(_("Print"))) if dlg.exec_(): self.webview.print_(printer) def slotShowContextMenu(self, pos): hit = self.webview.page().currentFrame().hitTestContent(pos) menu = QMenu() if hit.linkUrl().isValid(): a = self.webview.pageAction(QWebPage.CopyLinkToClipboard) a.setIcon(icons.get("edit-copy")) a.setText(_("Copy &Link")) menu.addAction(a) menu.addSeparator() a = menu.addAction(icons.get("window-new"), _("Open Link in &New Window")) a.triggered.connect( (lambda url: lambda: self.slotNewWindow(url))(hit.linkUrl())) else: if hit.isContentSelected(): a = self.webview.pageAction(QWebPage.Copy) a.setIcon(icons.get("edit-copy")) a.setText(_("&Copy")) menu.addAction(a) menu.addSeparator() a = menu.addAction(icons.get("window-new"), _("Open Document in &New Window")) a.triggered.connect((lambda url: lambda: self.slotNewWindow(url))( self.webview.url())) if menu.actions(): menu.exec_(self.webview.mapToGlobal(pos)) def slotNewWindow(self, url): helpers.openUrl(url)
def readData(self, size): if self.bytes_read >= len(self.content): return None data = self.content[self.bytes_read:self.bytes_read + size] self.bytes_read += len(data) return data def manager(self): return self.parent() if __name__ == '__main__': app = QApplication(sys.argv) gs = QtWebKit.QWebSettings.globalSettings() gs.setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True) gs.setAttribute(QtWebKit.QWebSettings.JavascriptEnabled, True) gs.setAttribute(QtWebKit.QWebSettings.AutoLoadImages, True) gs.setAttribute(QtWebKit.QWebSettings.JavascriptCanOpenWindows, True) gs.setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True) gs.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True) wv = QWebView() enam = NetworkAccessManager() wv.page().setNetworkAccessManager(enam) wv.show() wv.setUrl(QUrl("http://localhost/")) app.exec()
class PDFWriter(QObject): # {{{ def __init__(self, opts, log, cover_data=None, toc=None): from calibre.gui2 import must_use_qt from calibre.utils.podofo import get_podofo must_use_qt() QObject.__init__(self) self.logger = self.log = log self.podofo = get_podofo() self.doc = self.podofo.PDFDoc() self.loop = QEventLoop() self.view = QWebView() self.page = Page(opts, self.log) self.view.setPage(self.page) self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform) self.view.loadFinished.connect(self._render_html, type=Qt.QueuedConnection) for x in (Qt.Horizontal, Qt.Vertical): self.view.page().mainFrame().setScrollBarPolicy(x, Qt.ScrollBarAlwaysOff) self.render_queue = [] self.combine_queue = [] self.tmp_path = PersistentTemporaryDirectory(u'_pdf_output_parts') self.opts = opts self.cover_data = cover_data self.paged_js = None self.toc = toc def dump(self, items, out_stream, pdf_metadata): self.metadata = pdf_metadata self._delete_tmpdir() self.outline = Outline(self.toc, items) self.render_queue = items self.combine_queue = [] self.out_stream = out_stream self.insert_cover() self.render_succeeded = False self.current_page_num = self.doc.page_count() self.combine_queue.append(os.path.join(self.tmp_path, 'qprinter_out.pdf')) self.first_page = True self.setup_printer(self.combine_queue[-1]) QTimer.singleShot(0, self._render_book) self.loop.exec_() if self.painter is not None: self.painter.end() if self.printer is not None: self.printer.abort() if not self.render_succeeded: raise Exception('Rendering HTML to PDF failed') def _render_book(self): try: if len(self.render_queue) == 0: self._write() else: self._render_next() except: self.logger.exception('Rendering failed') self.loop.exit(1) def _render_next(self): item = unicode(self.render_queue.pop(0)) self.logger.debug('Processing %s...' % item) self.current_item = item load_html(item, self.view) def _render_html(self, ok): if ok: self.do_paged_render() else: # The document is so corrupt that we can't render the page. self.logger.error('Document cannot be rendered.') self.loop.exit(0) return self._render_book() def _pass_json_value_getter(self): val = json.dumps(self.bridge_value) return val def _pass_json_value_setter(self, value): self.bridge_value = json.loads(unicode(value)) _pass_json_value = pyqtProperty(str, fget=_pass_json_value_getter, fset=_pass_json_value_setter) def setup_printer(self, outpath): self.printer = self.painter = None printer = get_pdf_printer(self.opts, output_file_name=outpath) painter = QPainter(printer) zoomx = printer.logicalDpiX()/self.view.logicalDpiX() zoomy = printer.logicalDpiY()/self.view.logicalDpiY() painter.scale(zoomx, zoomy) pr = printer.pageRect() self.printer, self.painter = printer, painter self.viewport_size = QSize(pr.width()/zoomx, pr.height()/zoomy) self.page.setViewportSize(self.viewport_size) def do_paged_render(self): if self.paged_js is None: from calibre.utils.resources import compiled_coffeescript self.paged_js = compiled_coffeescript('ebooks.oeb.display.utils') self.paged_js += compiled_coffeescript('ebooks.oeb.display.indexing') self.paged_js += compiled_coffeescript('ebooks.oeb.display.paged') self.view.page().mainFrame().addToJavaScriptWindowObject("py_bridge", self) evaljs = self.view.page().mainFrame().evaluateJavaScript evaljs(self.paged_js) evaljs(''' py_bridge.__defineGetter__('value', function() { return JSON.parse(this._pass_json_value); }); py_bridge.__defineSetter__('value', function(val) { this._pass_json_value = JSON.stringify(val); }); document.body.style.backgroundColor = "white"; paged_display.set_geometry(1, 0, 0, 0); paged_display.layout(); paged_display.fit_images(); ''') mf = self.view.page().mainFrame() start_page = self.current_page_num if not self.first_page: start_page += 1 while True: if not self.first_page: if self.printer.newPage(): self.current_page_num += 1 self.first_page = False mf.render(self.painter) try: nsl = int(evaljs('paged_display.next_screen_location()')) except (TypeError, ValueError): break if nsl <= 0: break evaljs('window.scrollTo(%d, 0)'%nsl) self.bridge_value = tuple(self.outline.anchor_map[self.current_item]) evaljs('py_bridge.value = book_indexing.anchor_positions(py_bridge.value)') amap = self.bridge_value if not isinstance(amap, dict): amap = {} # Some javascript error occurred self.outline.set_pos(self.current_item, None, start_page, 0) for anchor, x in amap.iteritems(): pagenum, ypos = x self.outline.set_pos(self.current_item, anchor, start_page + pagenum, ypos) def append_doc(self, outpath): doc = self.podofo.PDFDoc() with open(outpath, 'rb') as f: raw = f.read() doc.load(raw) self.doc.append(doc) def _delete_tmpdir(self): if os.path.exists(self.tmp_path): shutil.rmtree(self.tmp_path, True) self.tmp_path = PersistentTemporaryDirectory('_pdf_output_parts') def insert_cover(self): if not isinstance(self.cover_data, bytes): return item_path = os.path.join(self.tmp_path, 'cover.pdf') printer = get_pdf_printer(self.opts, output_file_name=item_path, for_comic=True) self.combine_queue.insert(0, item_path) p = QPixmap() p.loadFromData(self.cover_data) if not p.isNull(): painter = QPainter(printer) draw_image_page(printer, painter, p, preserve_aspect_ratio=self.opts.preserve_cover_aspect_ratio) painter.end() self.append_doc(item_path) printer.abort() def _write(self): self.painter.end() self.printer.abort() self.painter = self.printer = None self.append_doc(self.combine_queue[-1]) try: self.doc.creator = u'%s %s [http://calibre-ebook.com]'%( __appname__, __version__) self.doc.title = self.metadata.title self.doc.author = self.metadata.author if self.metadata.tags: self.doc.keywords = self.metadata.tags self.outline(self.doc) self.doc.save_to_fileobj(self.out_stream) self.render_succeeded = True finally: self._delete_tmpdir() self.loop.exit(0)
class Window(QWidget): session = dict() def __init__(self, config): super(Window, self).__init__() self.config = config self.base_uri = QUrl.fromLocalFile(os.path.dirname(__file__)).toString() # initial web view add handle all link and form submitted self.web_view = QWebView(self) self.web_view.setPage(WebPage()) self.web_view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.web_view.page().linkClicked.connect(self.link_clicked) # self.web_view.page().urlChanged.connect(self.url_changed) self.web_view.page().loadFinished.connect(self.load_finished) self.web_view.page().loadStarted.connect(self.load_started) self.web_view.page().form_submitted.connect(self.handle_form_submitted) self.web_view.page().request_reload.connect(self.handle_reload) # initial template lookup self.tempalte_lookup = TemplateLookup( directories=[self.config.settings.get("mako.directories")], module_directory=self.config.settings.get("mako.module_directory"), input_encoding="utf-8", ) # layout attribute layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # add debug inspector if self.config.settings.get("debug", False): self.setup_inspector() self.splitter = QSplitter(self) self.splitter.setOrientation(Qt.Vertical) layout.addWidget(self.splitter) self.splitter.addWidget(self.web_view) self.splitter.addWidget(self.web_inspector) else: layout.addWidget(self.web_view) def setup_inspector(self): """ This code from http://agateau.com/2012/02/03/pyqtwebkit-experiments-part-2-debugging/ """ page = self.web_view.page() page.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True) self.web_inspector = QWebInspector(self) self.web_inspector.setPage(page) shortcut = QShortcut(self) shortcut.setKey(Qt.Key_F12) shortcut.activated.connect(self.toggle_inspector) self.web_inspector.setVisible(False) def toggle_inspector(self): self.web_inspector.setVisible(not self.web_inspector.isVisible()) def handle_form_submitted(self, qurl, elements=dict()): # print("\n\ngot url: ", qurl) qqurl = QUrlQuery(qurl) for key, value in qqurl.queryItems(): elements[key] = value self.render(qurl.path(), elements) # do stuff with elements... # for item in elements.items(): # print ("got: ", item) def handle_reload(self, qurl): self.render(qurl.path()) def load_started(self): """""" # print("load_started ->: ", self.web_view.url()) def load_finished(self, finished): """""" # print("load_finished ->: ", finished) # if finished: # self.web_view.setUrl(QUrl('/login')) # def url_changed(self, qurl): # '''''' # print("url_changed ->: ", qurl) def link_clicked(self, qurl): # print("link_clicked ->: ", qurl) qqurl = QUrlQuery(qurl) elements = {} # print("got link_clicked url: ", qurl) for key, value in qqurl.queryItems(): elements[key] = value self.render(qurl.path(), elements) # self.render(qurl.path()) def render(self, url, args=None): self.config.current_route_path = url print("current_route_path: ", self.config.current_route_path) logger.debug("url: %s" % url) route = self.config.get_route(url) logger.debug("view: %s" % route) if route is not None: view = route.get("view") context_obj = context.ResourceContext(self.config, self.session) context_obj.add_args(args) try: response = view(context_obj) except Exception as e: if e.args[0] == "Request Exit": self.close() return logger.exception(e) # need error page return self.link_clicked("/home") if not isinstance(response, dict): if isinstance(response, QUrl): return self.link_clicked(response) # url = response.path() # print('window url', url) # return self.render(url) # else: # # need error page # return self.render('/login') logger.debug("response: %s" % response) template = self.tempalte_lookup.get_template(self.config.get_route(url).get("renderer")) response["request"] = context_obj response["base_uri"] = self.base_uri html = template.render(**response) self.web_view.setHtml(html, QUrl("file://" + url)) # self.web_view.setHtml(html) # self.web_view.load(a) def welcome(self): context_obj = context.ResourceContext(self.config, self.session) return self.link_clicked(context_obj.redirect_url("login"))
class Spdom(WizardWidget): drag_label = "Spatial Domain <spdom>" acceptable_tags = ["spdom", "bounding"] ui_class = UI_spdom.Ui_fgdc_spdom def __init__(self, root_widget=None): self.east = 180 self.west = -180 self.north = 90 self.south = -90 self.valid = True super(self.__class__, self).__init__() self.schema = "bdp" self.root_widget = root_widget self.after_load = False self.in_xml_load = False self.has_rect = True self.completer = QCompleter() self.ui.fgdc_descgeog.setCompleter(self.completer) self.model = QStringListModel() self.completer.setModel(self.model) self.completer.setCaseSensitivity(0) fname = utils.get_resource_path("spatial/BNDCoords.csv") self.bnds_df = pd.read_csv(fname) self.model.setStringList(self.bnds_df["Name"]) self.completer.popup().clicked.connect(self.on_completer_activated) self.completer.popup().selectionModel().selectionChanged.connect( self.on_completer_activated) # self.completer.popup().activated.connect(self.on_completer_activated) def build_ui(self): """ Build and modify this widget's GUI Returns ------- None """ self.ui = self.ui_class() self.ui.setupUi(self) if platform.system() == "Darwin": map_fname = utils.get_resource_path("leaflet/map_mac.html") else: map_fname = utils.get_resource_path("leaflet/map.html") try: self.view = QWebView() self.view.page().mainFrame().addToJavaScriptWindowObject( "Spdom", self) self.view.setUrl(QUrl.fromLocalFile(map_fname)) self.frame = self.view.page().mainFrame() self.view.load( QUrl.fromLocalFile(QtCore.QDir.current().filePath(map_fname))) except AttributeError: self.view = QWebView() self.view.load( QUrl.fromLocalFile(QtCore.QDir.current().filePath(map_fname))) channel = QWebChannel(self.view.page()) jstr = """ var spdom; new QWebChannel(qt.webChannelTransport, function (channel) { spdom = channel.objects.spdom; });""" self.view.page().setWebChannel(channel) self.evaluate_js(jstr) channel.registerObject("spdom", self) self.ui.verticalLayout_3.addWidget(self.view) # setup drag-drop functionality for this widget and all it's children. self.setup_dragdrop(self) self.add_rect() self.raise_() def connect_events(self): self.ui.fgdc_eastbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_westbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_northbc.editingFinished.connect(self.coord_updated) self.ui.fgdc_southbc.editingFinished.connect(self.coord_updated) def on_completer_activated(self, model_index): try: cur_descgeog = model_index.data() except AttributeError: try: cur_descgeog = model_index.indexes()[0].data() except: return try: if self.bnds_df["Name"].str.contains(cur_descgeog).any(): self.ui.fgdc_eastbc.setText( str( float(self.bnds_df[self.bnds_df["Name"] == cur_descgeog]["east"]))) self.ui.fgdc_westbc.setText( str( float(self.bnds_df[self.bnds_df["Name"] == cur_descgeog]["west"]))) self.ui.fgdc_northbc.setText( str( float(self.bnds_df[self.bnds_df["Name"] == cur_descgeog]["north"]))) self.ui.fgdc_southbc.setText( str( float(self.bnds_df[self.bnds_df["Name"] == cur_descgeog]["south"]))) self.add_rect() self.update_map() except: pass # this is a convenience function. # If anything at all happens pass silently def complete_name(self): self.view.page().runJavaScript("addRect();", js_callback) def coord_updated(self): good_coords = self.all_good_coords() try: cur_name = self.sender().objectName() if "fgdc" not in cur_name: return cur_value = self.sender().text() except AttributeError: cur_name = "" cur_value = "" try: cur_value = float(cur_value) except ValueError: pass msg = "" if type(cur_value) != float and cur_value != "": msg = "number entered must be numeric only" elif cur_value == "": msg = "" elif cur_name in ["fgdc_westbc", "fgdc_eastbc" ] and -180 >= cur_value >= 180: msg = "East or West coordinate must be within -180 and 180" elif cur_name in ["fgdc_southbc", "fgdc_northbc" ] and -90 >= cur_value >= 90: msg = "North and South coordinates must be within -90 and 90" elif cur_name == "fgdc_southbc": try: north = float(self.ui.fgdc_northbc.text()) if north <= cur_value: msg = "North coordinate must be greater than South coordinate" except ValueError: pass elif cur_name == "fgdc_northbc": try: south = float(self.ui.fgdc_southbc.text()) if south >= cur_value: msg = "North coordinate must be greater than South coordinate" except ValueError: pass if msg: QMessageBox.warning(self, "Problem bounding coordinates", msg) if good_coords: self.add_rect() else: self.remove_rect() return self.update_map() def update_map(self): jstr = """east = {eastbc}; west = {westbc}; south = {southbc}; north = {northbc}; updateMap(); fitMap(); """.format( **{ "eastbc": self.ui.fgdc_eastbc.text(), "westbc": self.ui.fgdc_westbc.text(), "northbc": self.ui.fgdc_northbc.text(), "southbc": self.ui.fgdc_southbc.text(), }) self.evaluate_js(jstr) def add_rect(self): jstr = """addRect();""" self.evaluate_js(jstr) def remove_rect(self): if self.has_rect: self.has_rect = False jstr = """removeRect()""" self.evaluate_js(jstr) def evaluate_js(self, jstr): """ :param jstr: :return: """ try: self.frame.evaluateJavaScript(jstr) except: self.view.page().runJavaScript(jstr, js_callback) @pyqtSlot(float, float) def on_ne_move(self, lat, lng): if self.in_xml_load: n, e = lat, lng try: s = float(self.ui.fgdc_southbc.text()) w = float(self.ui.fgdc_westbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_eastbc.setText(bounds[1]) self.ui.fgdc_northbc.setText(bounds[2]) except: pass @pyqtSlot(float, float) def on_nw_move(self, lat, lng): if self.in_xml_load: n, w = lat, lng try: s = float(self.ui.fgdc_southbc.text()) e = float(self.ui.fgdc_eastbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_westbc.setText(bounds[0]) self.ui.fgdc_northbc.setText(bounds[2]) except: pass @pyqtSlot(float, float) def on_se_move(self, lat, lng): if self.in_xml_load: s, e = lat, lng try: n = float(self.ui.fgdc_northbc.text()) w = float(self.ui.fgdc_westbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_eastbc.setText(bounds[1]) self.ui.fgdc_southbc.setText(bounds[3]) except: pass @pyqtSlot(float, float) def on_sw_move(self, lat, lng): if self.in_xml_load: s, w = lat, lng try: n = float(self.ui.fgdc_northbc.text()) e = float(self.ui.fgdc_eastbc.text()) bounds = spatial_utils.format_bounding((w, e, n, s)) self.ui.fgdc_westbc.setText(bounds[0]) self.ui.fgdc_southbc.setText(bounds[3]) except: pass def switch_schema(self, schema): self.schema = schema if schema == "bdp": self.ui.fgdc_descgeog.show() self.ui.descgeog_label.show() self.ui.descgeog_star.show() else: self.ui.fgdc_descgeog.hide() self.ui.descgeog_label.hide() self.ui.descgeog_star.hide() def all_good_coords(self): try: if -180 > float(self.ui.fgdc_westbc.text()) > 180: return False if -180 > float(self.ui.fgdc_eastbc.text()) > 180: return False if -90 > float(self.ui.fgdc_southbc.text()) > 90: return False if -90 > float(self.ui.fgdc_northbc.text()) > 90: return False if float(self.ui.fgdc_northbc.text()) <= float( self.ui.fgdc_southbc.text()): return False return True except: return False def clear_widget(self): super(self.__class__, self).clear_widget() # self.view.page().mainFrame().addToJavaScriptWindowObject("Spdom", self) # map_fname = utils.get_resource_path('leaflet/map.html') # self.view.setUrl(QUrl.fromLocalFile(map_fname)) def showEvent(self, e): if not self.after_load: self.add_rect() self.update_map() jstr = "sw_marker.openPopup();" self.evaluate_js(jstr) self.after_load = True def to_xml(self): spdom = xml_node("spdom") if self.schema == "bdp": descgeog = xml_node("descgeog", text=self.ui.fgdc_descgeog.text(), parent_node=spdom) bounding = xml_node("bounding", parent_node=spdom) westbc = xml_node("westbc", text=self.ui.fgdc_westbc.text(), parent_node=bounding) eastbc = xml_node("eastbc", text=self.ui.fgdc_eastbc.text(), parent_node=bounding) northbc = xml_node("northbc", text=self.ui.fgdc_northbc.text(), parent_node=bounding) southbc = xml_node("southbc", text=self.ui.fgdc_southbc.text(), parent_node=bounding) if self.original_xml is not None: boundalt = xml_utils.search_xpath(self.original_xml, "bounding/boundalt") if boundalt is not None: spdom.append(deepcopy(boundalt)) dsgpoly_list = xml_utils.search_xpath(self.original_xml, "dsgpoly", only_first=False) for dsgpoly in dsgpoly_list: spdom.append(deepcopy(dsgpoly)) return spdom def from_xml(self, spdom): self.in_xml_load = False self.original_xml = spdom self.clear_widget() utils.populate_widget(self, spdom) contents = xml_utils.node_to_dict(spdom, add_fgdc=False) if "bounding" in contents: contents = contents["bounding"] try: if self.all_good_coords(): self.add_rect() self.update_map() else: self.remove_rect() except KeyError: self.remove_rect() self.in_xml_load = True
class Application(object): settings = None html_template = None geometry = None loaded_plugins = None command_handlers = None metadata = None def _on_navigation(self, url): url = str(url.toString()) handler, cmd = url.split('::') if handler and handler in self.command_handlers: return self.command_handlers[handler].handle(cmd) else: self.web_view.load(QUrl(url)) return False def render_template(self): metadata = self.metadata output = self.html_template # @TODO: create shell::{some command} parser # MSG = MSG.replace('%UNAME%', shell_cmd('uname -a').decode('UTF-8')) if metadata: output = output.replace('%ARTIST%', metadata['artist']) output = output.replace('%ALBUM%', metadata['album']) output = output.replace('%TITLE%', metadata['title']) output = output.replace('%ARTURL%', metadata['arturl']) self.web_view.setHtml(output) def _read_config(self): # @TODO: here it will be config parsing, now it's a placeholder self.settings = SETTINGS try: self.html_template = open('index.html', 'r').read() except Exception: print('Error in template') self.html_template = DEFAULT_MSG left = self.settings.get( "left", self.app.desktop().screenGeometry().width() - self.settings["right"] - self.settings["width"] ) self.settings['geometry'] = ( left, self.settings["top"], self.settings["width"], self.settings["height"] ) self.html_template = self.html_template.replace( '%WIDTH%', str(self.settings["width"])) self.html_template = self.html_template.replace('%PATH%', APP_PATH) def __init__(self): self.app = QApplication(sys.argv) self.window = QMainWindow() self.metadata = {} self.command_handlers = {} self._read_config() if get_desktop_name() in ['openbox', 'pekwm']: # windowAttribute for openbox/pekwm WM self.window.setAttribute(Qt.WA_X11NetWmWindowTypeDesktop) else: # windowAttribute for any other DE like xfce, gnome, unity, kde etc self.window.setAttribute(Qt.WA_X11NetWmWindowTypeDock) self.window.setWindowFlags(Qt.WindowStaysOnBottomHint) self.window.setAttribute(Qt.WA_TranslucentBackground) self.web_view = QWebView() # trasparent webview palette = self.web_view.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.web_view.page().setPalette(palette) self.web_view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.web_view.linkClicked.connect(self._on_navigation) self.web_view.setHtml(self.html_template) self.window.setGeometry(*self.settings['geometry']) self.window.setCentralWidget(self.web_view) self.window.show() self.loaded_plugins = [ CmdHandler(self), ClementineDBusInterface(self), ] sys.exit(self.app.exec_())
class HelpWidget(QWidget) : def __init__( self ) : super().__init__() # no parent; we are a standalone window # Set window title to translated value self.setWindowTitle( _TR( "title of help viewer window", "PPQT Help Viewer" ) ) # Initialize saved geometry, see showEvent() self.last_shape = None # Initialize find string, see find_action(). self.find_text = None self.view = QWebView() hb = QHBoxLayout() hb.addWidget(self.view) self.setLayout(hb) # Look for the help text file and load it if found. paths.notify_me( self.path_change ) self.help_url = None self.path_change( ) # force a load now # Slot to receive the pathChanged signal from the paths module. That # signal is given when any of the standard paths are changed. Use the # extras path to (re)load the help file. # # This is also called from __init__ to do the initial load, and from # keyEvent processing to implement Back when the history stack is empty. # # The file we need to load is not actually ppqt2help.html but the # file sphinx/index.html. QWebView only shows the nice trimmings if we # open that. def path_change( self, code='extras' ) : if code == 'extras' : extras_path = paths.get_extras_path() help_path = os.path.join( extras_path , 'ppqt2help.html' ) sphinx_path = os.path.join( extras_path, 'sphinx', 'index.html' ) if os.access( help_path, os.R_OK ) and os.access( sphinx_path, os.R_OK ) : # the help file exists. Save a QUrl describing it, and load it. self.help_url = QUrl.fromLocalFile( sphinx_path ) self.view.load( self.help_url ) else : self.help_url = None self.view.setHtml( DEFAULT_HTML ) def closeEvent( self, event ) : self.last_shape = self.saveGeometry() event.ignore() self.hide() return super().closeEvent(event) def showEvent( self, event ) : if self.last_shape : # is not None self.restoreGeometry( self.last_shape ) def keyPressEvent( self, event ) : kkey = int( int(event.modifiers()) & C.KEYPAD_MOD_CLEAR) | int(event.key() ) if kkey == C.CTL_F : event.accept() self.find_action() elif kkey == C.CTL_G : event.accept() self.find_next_action() elif kkey == C.CTL_SHFT_G : event.accept() self.find_prior_action() elif kkey in C.KEYS_WEB_BACK : event.accept() if self.view.history().canGoBack() : self.view.history().back() elif self.help_url is not None: self.view.load( self.help_url ) else: # try to reload the default text self.path_change() elif kkey in C.KEYS_WEB_FORWARD : if self.view.history().canGoForward() : self.view.history().forward() else: utilities.beep() else : super().keyPressEvent(event) # Methods for a basic Find operation. Other modules e.g. noteview have a # custom Edit menu with Find/Next as menu actions. Here we do not support # any editing actions, so support Find/Next/Prior only via keystrokes. # # Use the simple find dialog in utilities to get a string to look for. # Initialize the dialog with up to 40 chars of the current selection. If # the user clicks Cancel in the Find dialog, self.find_text is None; if # the user just clears the input area and clicks OK the find_text is an # empty string. In either case do nothing. # # Following a successful find, the found text is selected in the view, so # if you do ^f again without disturbing that selection, that text is back # in the dialog to be used or replaced. So ^f+Enter is the same as ^g. FIND_NORMAL = QWebPage.FindWrapsAroundDocument FIND_PRIOR = QWebPage.FindWrapsAroundDocument | QWebPage.FindBackward def find_action(self): prep_text = self.view.selectedText()[:40] self.find_text = utilities.get_find_string( _TR('Help viewer find dialog','Text to find'), self, prep_text) if self.find_text : # is not None nor an empty string self._do_find( self.FIND_NORMAL ) # For ^g Find Next, if there is no active find-text pretend that ^f was # hit. If there was a prior search, repeat the search. def find_next_action(self): if self.find_text : # is not None nor an empty string self._do_find( self.FIND_NORMAL ) else : self.find_action() # for ^G Find Prior, same as for ^g but backward. def find_prior_action(self): if self.find_text : self._do_find( self.FIND_PRIOR ) else : self.find_action() # The actual search, factored out of the above actions. Because the # search wraps around, if it fails, it fails, and that's that. def _do_find( self, find_flags ) : if not self.view.page().findText( self.find_text, find_flags ): utilities.beep()
class BrowserController(QObject): def __init__(self, parent, fullscreen, sizes): """ This constructor function initializes a layout of the Arius output module but doesn`t displays it on the screen. Firstly, it creates a new app and detects screens configuration. Then, QGridLayout is created and its appearance is configured: margins and spaces between elements is set to 0. After that, we create three QWebViews: one for our main content view and two others for header and footer views. Immideately after creating these instances we assign them their heights according to percentages given by user and dimensions of the screen. Next, we remove scroll bars from top and bottom views and load predefined pages into them. Then we allow QWebKit to run all extensions it needs to render the page. After that, wee set the layout design as a grid of three rows. Finally, we create an updater object which will run in another stream and a timer instance which checks that stream for new commands from the server, and in case if there`s some update handles it. """ super(BrowserController, self).__init__() if not fullscreen and not sizes: print 'You must initialize windows size' raise Exception self._fullscreen = fullscreen if sizes: self._screen_width = sizes[0] self._screen_height = sizes[1] self._app = QtWidgets.QApplication(sys.argv) self._app.setStyle("Fusion") if self._fullscreen: self._get_screen_height() self._layout = QGridLayout() self._layout.setSpacing(0) self._layout.setContentsMargins(0, 0, 0, 0) self._main_browser = QWebView() main_page = FakePage(self) self._main_browser.setPage(main_page) self._top_browser = QWebView() self._bottom_browser = QWebView() self._top_browser_height = config['output_header_height'] * self._screen_height self._bottom_browser_height = config['output_footer_height'] * self._screen_height self._top_browser.setMaximumHeight(self._top_browser_height) self._top_browser.setMinimumHeight(self._top_browser_height) self._bottom_browser.setMaximumHeight(self._bottom_browser_height) self._bottom_browser.setMinimumHeight(self._bottom_browser_height) self._top_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._main_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._main_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff) self._bottom_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._top_browser_load_url( config['flask_server_home'] + config['output_browser_top_page']) self._bottom_browser_load_url( config['flask_server_home'] + config['output_browser_bottom_page']) self._main_browser.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) self._main_browser.settings().setAttribute(QWebSettings.PluginsEnabled, True) QWebSettings.setObjectCacheCapacities(0, 0, 0) self._main_browser.settings().setAttribute( QWebSettings.AcceleratedCompositingEnabled, True) self._main_browser.settings().setAttribute(QWebSettings.WebGLEnabled, True) self._layout.addWidget(self._top_browser, 1, 0) self._layout.addWidget(self._main_browser, 2, 0) self._layout.addWidget(self._bottom_browser, 3, 0) self._parent = parent self._parent.load_url.connect(self._load_url) self._parent.js_execution.connect(self._execute_js) self._parent.zooming.connect(self._zoom) def run(self): self._main_window = QWidget() # create a window as a QWidget self._main_window.setLayout(self._layout) # assign a layout to it if self._fullscreen: self._main_window.showFullScreen() # set full screen enabled else: self._main_window.resize(self._screen_width, self._screen_height) self._main_window.show() # and finally, show the window # and set a trigger to exit the app, as window is closed sys.exit(self._app.exec_()) def _get_screen_height(self): if self._fullscreen: output = subprocess.Popen('xrandr | grep "\*" | cut -d" " -f4', shell=True, stdout=subprocess.PIPE).communicate()[0] output = [int(val) for val in output.split('x')] self._screen_height = output[1] self._screen_width = output[0] else: output = [self._width, self._height] return output[0], output[1] def _zoom(self, factor): self._main_browser.page().mainFrame().setZoomFactor(factor) def _top_browser_load_url(self, url): self._top_browser.load(QUrl(url)) def _bottom_browser_load_url(self, url): self._bottom_browser.load(QUrl(url)) def _load_url(self, url): logger.debug('OPENING URL %s', url) self._main_browser.load(QUrl(url)) def _execute_js(self, string_js): logger.debug(string_js) self._main_browser.page().mainFrame().evaluateJavaScript(string_js)
class WebBrowser(QMainWindow): adjTitle = pyqtSignal(str) setProg = pyqtSignal(int) finishLoad = pyqtSignal(str) def __init__(self, url, parent=None): super(WebBrowser, self).__init__(parent) self.progress = 0 fd = QFile(":/jquery.min.js") if fd.open(QIODevice.ReadOnly | QFile.Text): self.jQuery = QTextStream(fd).readAll() fd.close() else: self.jQuery = '' QNetworkProxyFactory.setUseSystemConfiguration(True) self.view = QWebEngineView(self) self.view.load(QUrl(url)) self.view.loadFinished.connect(self.adjustLocation) self.view.titleChanged.connect(self.adjustTitle) self.view.loadProgress.connect(self.setProgress) self.view.loadFinished.connect(self.finishLoading) self.locationEdit = QLineEdit(self) self.locationEdit.setSizePolicy( QSizePolicy.Expanding, self.locationEdit.sizePolicy().verticalPolicy()) self.locationEdit.returnPressed.connect(self.changeLocation) toolBar = self.addToolBar("Navigation") toolBar.addAction(self.view.pageAction(QWebEnginePage.Back)) toolBar.addAction(self.view.pageAction(QWebEnginePage.Forward)) toolBar.addAction(self.view.pageAction(QWebEnginePage.Reload)) toolBar.addAction(self.view.pageAction(QWebEnginePage.Stop)) toolBar.addWidget(self.locationEdit) viewMenu = self.menuBar().addMenu("&View") viewSourceAction = QAction("Page Source", self) viewSourceAction.triggered.connect(self.viewSource) viewMenu.addAction(viewSourceAction) effectMenu = self.menuBar().addMenu("&Effect") effectMenu.addAction("Highlight all links", self.highlightAllLinks) self.rotateAction = QAction(self.style().standardIcon( QStyle.SP_FileDialogDetailedView), "Turn images upside down", self, checkable=True, toggled=self.rotateImages) effectMenu.addAction(self.rotateAction) toolsMenu = self.menuBar().addMenu("&Tools") toolsMenu.addAction("Remove GIF images", self.removeGifImages) toolsMenu.addAction("Remove all inline frames", self.removeInlineFrames) toolsMenu.addAction("Remove all object elements", self.removeObjectElements) toolsMenu.addAction("Remove all embedded elements", self.removeEmbeddedElements) self.setCentralWidget(self.view) def viewSource(self): accessManager = self.view.page().networkAccessManager() request = QNetworkRequest(self.view.url()) reply = accessManager.get(request) reply.finished.connect(self.slotSourceDownloaded) def slotSourceDownloaded(self): reply = self.sender() self.textEdit = QTextEdit() self.textEdit.setAttribute(Qt.WA_DeleteOnClose) self.textEdit.show() self.textEdit.setPlainText(QTextStream(reply).readAll()) self.textEdit.resize(600, 400) reply.deleteLater() def adjustLocation(self): self.locationEdit.setText(self.view.url().toString()) def changeLocation(self): url = QUrl.fromUserInput(self.locationEdit.text()) self.view.load(url) self.view.setFocus() def adjustTitle(self): if 0 < self.progress < 100: self.setWindowTitle("{0} ({1}%%)".format(self.view.title(), self.progress)) else: self.setWindowTitle(self.view.title()) def setProgress(self, p): self.progress = p self.setProg.emit(self.progress) self.adjustTitle() def finishLoading(self): self.progress = 100 self.adjustTitle() # self.view.page().mainFrame().evaluateJavaScript(self.jQuery) self.rotateImages(self.rotateAction.isChecked()) def highlightAllLinks(self): code = """$('a').each( function () { $(this).css('background-color', 'yellow') } )""" self.view.page().javaScriptPrompt(code) def rotateImages(self, invert): if invert: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(180deg)') } )""" else: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(0deg)') } )""" # self.view.page().mainFrame().evaluateJavaScript(code) def removeGifImages(self): code = "$('[src*=gif]').remove()" self.view.page().runJavaScript(code) def removeInlineFrames(self): code = "$('iframe').remove()" self.view.page().runJavaScript(code) def removeObjectElements(self): code = "$('object').remove()" self.view.page().runJavaScript(code) def removeEmbeddedElements(self): code = "$('embed').remove()" self.view.page().runJavaScript(code)
ba = len(self.content) - self.bytes_read + super().bytesAvailable() return ba def readData(self, size): if self.bytes_read >= len(self.content): return None data = self.content[self.bytes_read:self.bytes_read + size] self.bytes_read += len(data) return data def manager(self): return self.parent() if __name__ == '__main__': app = QApplication(sys.argv) gs = QtWebKit.QWebSettings.globalSettings() gs.setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True) gs.setAttribute(QtWebKit.QWebSettings.JavascriptEnabled, True) gs.setAttribute(QtWebKit.QWebSettings.AutoLoadImages, True) gs.setAttribute(QtWebKit.QWebSettings.JavascriptCanOpenWindows, True) gs.setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True) gs.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True) wv = QWebView() enam = NetworkAccessManager() wv.page().setNetworkAccessManager(enam) wv.show() wv.setUrl(QUrl("http://localhost/")) app.exec()
class OutputInterface: def __init__(self, fullscreen=False, sizes=None): """ This constructor function initializes a layout of the Arius output module but doesn`t displays it on the screen. Firstly, it creates a new app and detects screens configuration. Then, QGridLayout is created and its appearance is configured: margins and spaces between elements is set to 0. After that, we create three QWebViews: one for our main content view and two others for header and footer views. Immideately after creating these instances we assign them their heights according to percentages given by user and dimensions of the screen. Next, we remove scroll bars from top and bottom views and load predefined pages into them. Then we allow QWebKit to run all extensions it needs to render the page. After that, wee set the layout design as a grid of three rows. Finally, we create an updater object which will run in another stream and a timer instance which checks that stream for new commands from the server, and in case if there`s some update handles it. """ if not fullscreen and not sizes: print 'You must initialize windows size' raise Exception self._fullscreen = fullscreen if sizes: self._screen_width = sizes[0] self._screen_height = sizes[1] self._app = QtWidgets.QApplication(sys.argv) self._app.setStyle("Fusion") if self._fullscreen: self._get_screen_height() self._layout = QGridLayout() # create a main view of an app self._layout.setSpacing(0) # and do some design settings self._layout.setContentsMargins(0, 0, 0, 0) self._main_browser = QWebView() # create a main content view # and initialize zoom factor variable which will be used to control # zoom main_page = FakeBrowser(self) self._main_browser.setPage(main_page) self._zoom_factor = 1 self._top_browser = QWebView() # and create top and bottom views self._bottom_browser = QWebView() self._top_browser_height = config[ 'output_header_height'] * self._screen_height # calculate views sizes self._bottom_browser_height = config[ 'output_footer_height'] * self._screen_height self._top_browser.setMaximumHeight( self._top_browser_height) # and assign them to the views self._top_browser.setMinimumHeight(self._top_browser_height) self._bottom_browser.setMaximumHeight(self._bottom_browser_height) self._bottom_browser.setMinimumHeight(self._bottom_browser_height) self._top_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) # remove the scroll bars self._main_browser.page().mainFrame().setScrollBarPolicy(QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._main_browser.page().mainFrame().setScrollBarPolicy(QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff) self._bottom_browser.page().mainFrame().setScrollBarPolicy( QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) self._top_browser_load_url( config['flask_server_home'] + config['output_browser_top_page']) # load default design self._bottom_browser_load_url( config['flask_server_home'] + config['output_browser_bottom_page']) self._main_browser.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) # enable console self._main_browser.settings().setAttribute( QWebSettings.PluginsEnabled, True) # enable plugins QWebSettings.setObjectCacheCapacities(0, 0, 0) # disable caching self._main_browser.settings().setAttribute( QWebSettings.AcceleratedCompositingEnabled, True) self._main_browser.settings().setAttribute( QWebSettings.WebGLEnabled, True) self._layout.addWidget(self._top_browser, 1, 0) # set views positions self._layout.addWidget(self._main_browser, 2, 0) self._layout.addWidget(self._bottom_browser, 3, 0) # create a RLock object to syncronyze threads. self._lock = threading.RLock() # and create an updater object self._updater = OutputUpdater(self._lock) self._updater.start() # which is ran in another non-blocking stream self.timeoutTimer = QTimer() # create a timer to check for commands tCallback = functools.partial( self._handle_command) # set a timer`s function self.timeoutTimer.timeout.connect(tCallback) self.timeoutTimer.start( config['output_update_frequency']) # and start it # as no data is displayed on the main view - curent content type is # None self._cur_filetype = None # text to speech speaker self._speaker = None # audioplayer self._player = Player() def run(self): """ This method is called to show the output module window after initializing all views in __init__. """ self._main_window = QWidget() # create a window as a QWidget self._main_window.setLayout(self._layout) # assign a layout to it if self._fullscreen: self._main_window.showFullScreen() # set full screen enabled else: self._main_window.resize(self._screen_width, self._screen_height) self._main_window.show() # and finally, show the window # and set a trigger to exit the app, as window is closed sys.exit(self._app.exec_()) logger.info('Finished') def _get_screen_height(self): """ This method is used to get dimensions of user`s screen. To do this is uses system utilite 'xrandr' via subprocess module, which is not very nice way, however we didn`t find anything better. """ if self._fullscreen: output = subprocess.Popen('xrandr | grep "\*" | cut -d" " -f4', shell=True, stdout=subprocess.PIPE).communicate()[0] output = [int(val) for val in output.split('x')] # logger.debug('Screen dimensions are: {} x {}'.format(output[0], output[1])) self._screen_height = output[1] self._screen_width = output[0] else: output = [self._width, self._height] return output[0], output[1] def _handle_command(self): """ This is a method called by an updater timer each given interval of time. It checks updater object and in case if there`s some new command it proceeds it. """ command = self._updater.get_state() # get a new command if command[0] != 'none' and command[0] != None: # if there`s a command, handle it logger.info('Handling command {}'.format(command)) # self._updater.reset() # clear the updater object command buffer # Commands for opening all types of content are handled by # calling of _load_content method with specified content type. # start music play if it was stopped if command[0] == 'OPEN_PDF': self._player.play() self._load_content('local_pdf', command[1]) elif command[0] == 'OPEN_URL': self._player.play() self._load_content('external_url', command[1]) elif command[0] == 'OPEN_LOCAL_PAGE': self._player.play() self._load_content('local_url', command[1]) elif command[0] == 'OPEN_VIDEO': self._player.stop() self._load_content('local_video', command[1]) # Command to open a system scren (e.g. {'type': 'OPEN_SCREEN', 'command':'OPEN_IDLE'}) # will be proceeded by sending a 'command' to a _load_screen method, which does futher # work. elif command[0] == 'OPEN_SCREEN': self._player.play() self._load_screen(command[1]) # Zoom commands are handled each with its own method. elif command[0] == 'ZOOM_IN': self._main_browser_zoom_in() elif command[0] == 'ZOOM_OUT': self._main_browser_zoom_out() elif command[0] == 'NO_ZOOM': self._main_browser_reset_zoom() # as well as scroll. elif command[0] == 'SCROLL_DOWN': self._main_browser_scroll_down() elif command[0] == 'SCROLL_UP': self._main_browser_scroll_up() elif command[0] == 'SCROLL_LEFT': self._main_browser_scroll_left() elif command[0] == 'SCROLL_RIGHT': self._main_browser_scroll_right() elif command[0] == 'CONTINIOUS_SCROLL_UP': pass elif command[0] == 'CONTINIOUS_SCROLL_DOWN': pass elif command[0] == 'STOP_SCROLL': pass # Following two commands are responsible for playing # video and pausing it. As a second part of the command # name of the video file should be given. elif command[0] == 'PLAY': self._player.stop() self._video_play() elif command[0] == 'PAUSE': self._video_pause() # These two commands are used for increasing or # decreasing video volume. elif command[0] == 'VOLUME_UP': self._volume_up() elif command[0] == 'VOLUME_DOWN': self._volume_down() # and these two are for text-to-speech # The 'command' body should be a text you # you want to hear. elif command[0] == "SPEAK": print 'SPEAK' self._player.play() self._speak_text(command[1]) elif command[0] == "STOP_SPEAK": self._player.play() self._speak_stop() elif command[0] == 'NEXT_PAGE': self._main_browser_next_page() elif command[0] == 'PREV_PAGE': self._main_browser_prev_page() elif command[0] == 'MUTE': self._mute() elif command[0] == 'UNMUTE': self._unmute() else: logger.info('command not recognized {}'.format(command)) else: pass def _load_screen(self, screen_type): """ This method is responsible for opening system Arius screens. It receives a type of screen, server wants to open and then displays it on the main content view. """ self._main_browser_reset_zoom() # set zoom level to 1 in case if previous page was zoomed # this a default path to the arius server url = 'http://' + config['flask_server_address'] + \ ':' + config['flask_server_port'] if screen_type == 'IDLE': # and here in depend of type of the required screen # we set the last path of the URL to the exact screen url = url + config['flask_server_idle_address'] elif screen_type == 'ERROR': url = url + config['flask_server_error_address'] elif screen_type == 'SEARCH': url = url + config['flask_server_search_address'] elif screen_type == 'SPEAKING': url = url + config['flask_server_speaking_address'] else: # and if the target screen wasn`t recognized we can also handle it logger.info('WRONG SCREEN TYPE: {}'.format(screen_type)) logger.debug( 'Opening {} screen on following address {}'.format(screen_type, url)) self._main_browser.load(QUrl(url)) def _custom_ua(self, url): """ This function returns a custom user agent data which is stored in config.py file as 'output_user_agent' """ return config['output_user_agent'] def _load_content(self, content_type, content): """ This method is for displaying some content of such types: local web pages, remote web pages, local pdf`s, local videos. It should be called like in the following example to work correctly: self._load_content('local_video', 'some_video.mp4') """ self._main_browser_reset_zoom() # reset zoom if content_type == 'local_url': source = config['flask_server_home'] + \ config['flask_server_local_page_client'] + \ content # simply get the file from flask-server by its relative path-id # specify type of currently opened file for zoom and scroll methods self._cur_filetype = "webpage" elif content_type == 'external_url': self._cur_filetype = "webpage" # no needs required as, link should be given in # 'http://yoursite.com/yourpage' source = content elif content_type == 'local_pdf': # to render PDF`s we use PDF.js, so we open its page and send it a # path for the target file. source = config['flask_server_home'] + \ config['flask_server_local_page_client'] + \ content self._cur_filetype = "pdf" elif content_type == 'local_video': # in case of opening local videos we need to modify the path to the video in the source code of # the videoplayer, so don`t die after reading this code. It works just in the same style as other # filetypes, but in a very weird way. source = config['flask_server_home'] + \ config['flask_server_video_addr_client'] + content logger.info('Opening video at {}'.format(source)) self._cur_filetype = "video" # Set a custom user agent to avoid message about deprecated version of # browser self._main_browser.page().userAgentForUrl = self._custom_ua logger.info('Loading data on address: {}'.format(source)) # Create a request to be able to set user-agent data. Without # it, it`s impossible to customize request data. request = QNetworkRequest() request.setUrl(QUrl(source)) request.setRawHeader("USER-AGENT", config['output_user_agent']) # and finally load the result self._main_browser.load(request) def _top_browser_load_url(self, url): """ This method just loads a given URL in the top content view. URL must contain protocol and the address. e.g. http://placehold.it/400x200 or file://data/somepage.html """ logger.info('Loading {} to the top content view.'.format(url)) self._top_browser.load(QUrl(url)) def _bottom_browser_load_url(self, url): """ This method just loads a given URL in the bottom content view. URL must contain protocol and the address. e.g. http://placehold.it/400x200 or file: """ logger.info('Loading {} to the bottom content view.'.format(url)) self._bottom_browser.load(QUrl(url)) def _main_browser_scroll_down(self): """ This method is called to smoothly scroll the page down. In dependence of the current content type if provides different implementations of scroll, but generally it gives the same result. """ logger.debug('Scrolling main content view down') scroll_js = open("scroll.js", "r").read() self._main_browser.page().mainFrame().evaluateJavaScript(scroll_js) if self._cur_filetype == "pdf": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_vscroll_by(PDFViewerApplication.pdfViewer.container, 300, 1000);") elif self._cur_filetype == "webpage": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_vscroll_by(document.body, 300, 1000);") def _main_browser_scroll_up(self): """ This method is called to smoothly scroll the page up. In dependence of the current content type if provides different implementations of scroll, but generally it gives the same result. """ logger.debug('Scrolling main content view down') scroll_js = open("scroll.js", "r").read() self._main_browser.page().mainFrame().evaluateJavaScript(scroll_js) if self._cur_filetype == "pdf": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_vscroll_by(PDFViewerApplication.pdfViewer.container, -300, 1000);") elif self._cur_filetype == "webpage": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_vscroll_by(document.body, -300, 1000);") def _main_browser_scroll_left(self): """ This method is called to smoothly scroll the page left. In dependence of the current content type if provides different implementations of scroll, but generally it gives the same result. """ logger.debug('Scrolling main content view left') scroll_js = open("scroll.js", "r").read() self._main_browser.page().mainFrame().evaluateJavaScript(scroll_js) if self._cur_filetype == "pdf": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_hscroll_by(PDFViewerApplication.pdfViewer.container, -100, 1000);") elif self._cur_filetype == "webpage": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_hscroll_by(document.body, -100, 1000);") def _main_browser_scroll_right(self): """ This method is called to smoothly scroll the page right. In dependence of the current content type if provides different implementations of scroll, but generally it gives the same result. """ logger.debug('Scrolling main content view right') scroll_js = open("scroll.js", "r").read() self._main_browser.page().mainFrame().evaluateJavaScript(scroll_js) if self._cur_filetype == "pdf": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_hscroll_by(PDFViewerApplication.pdfViewer.container, 100, 1000);") elif self._cur_filetype == "webpage": self._main_browser.page().mainFrame().evaluateJavaScript("smooth_hscroll_by(document.body, 100, 1000);") def _main_browser_zoom_in(self): """ This methoud simply zooms main content view in. It works well with all content types. """ logger.debug('Zooming main content view in') if self._cur_filetype == "pdf": string_js = """PDFViewerApplication.zoomIn();""" self._main_browser.page().mainFrame().evaluateJavaScript(string_js) elif self._cur_filetype == "webpage": self._zoom_factor += .1 self._main_browser.page().mainFrame().setZoomFactor(self._zoom_factor) def _main_browser_reset_zoom(self): """ This method sets zoom level of the main content view to the default level. It should be called before loading a new page or in order to cancel any zoom changes. """ logger.debug('Resetting zoom in the main content view') if self._cur_filetype == "pdf": string_js = 'PDFViewerApplication.pdfViewer.currentScaleValue = "page-width"' self._main_browser.page().mainFrame().evaluateJavaScript(string_js) elif self._cur_filetype == "webpage": self._zoom_factor = 1 self._main_browser.page().mainFrame().setZoomFactor(self._zoom_factor) def _main_browser_zoom_out(self): """ This methoud simply zooms main content view out. It works well with all content types. """ logger.debug('Zooming main content view out') if self._cur_filetype == "pdf": string_js = """PDFViewerApplication.zoomOut();""" self._main_browser.page().mainFrame().evaluateJavaScript(string_js) elif self._cur_filetype == "webpage": self._zoom_factor -= .1 self._main_browser.page().mainFrame().setZoomFactor(self._zoom_factor) def _main_browser_next_page(self): logger.debug('Next page') script_js = """PDFViewerApplication.page++;""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _main_browser_prev_page(self): logger.debug('Previous page') script_js = """PDFViewerApplication.page--;""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _speak_text(self, input_text): """ This method is used to provide text-to-speech syntesizer for voice output. As a back-end of this method Mary TTS is used. Voices can be configured through config.py quit easy. """ logger.info('Speaking following text {}'.format(input_text)) if self._speaker is None: # if there wasn`t no tts requests before, create a TTS client self._speaker = Speaker(config['default_voice'], config[ "marytts_host"], config["marytts_port"]) # otherwise just stop previous speaking session (if Arius isn`t # speaking at the moment, nothing will crash) else: self._speaker.stop() # and send given text as a tts request. self._speaker.speak(input_text) def _speak_stop(self): """ This method simply stops current voice output. """ logger.info('Stop speaking') self._speaker.stop() def _video_play(self): """ This method is used to begin playing video on the current page. It should be run only if current content type is 'video'. """ logger.debug('Playing video') if self._cur_filetype == 'video': script_js = """video=document.getElementById("arius_videoplayer"); video.play()""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _video_pause(self): """ This method is used to pause currently playing video on the page. It should be run only if current content type is 'video'. """ logger.debug('Video paused') if self._cur_filetype == 'video': script_js = """video=document.getElementById("arius_videoplayer"); video.pause()""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _volume_up(self): """ This method is used to increase volume. It should be run only if current content type is 'video'. """ logger.debug('Increasing volume') if self._cur_filetype == 'video': script_js = """video=document.getElementById("arius_videoplayer"); video.volume+=0.2;""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _volume_down(self): """ This method is used to decrease volume. It should be run only if current content type is 'video'. """ logger.debug('Decreasing volume') if self._cur_filetype == 'video': script_js = """video=document.getElementById("arius_videoplayer"); video.volume-=0.2;""" self._main_browser.page().mainFrame().evaluateJavaScript(script_js) def _mute(self): if self._player: self._player.mute() if self._speaker: self._speaker.mute() def _unmute(self): if self._player: self._player.unmute() if self._speaker: self._speaker.unmute()
class Printing(QObject): def __init__(self, iterator, parent): QObject.__init__(self, parent) self.current_index = 0 self.iterator = iterator self.view = QWebView(self.parent()) self.mf = mf = self.view.page().mainFrame() for x in (Qt.Horizontal, Qt.Vertical): mf.setScrollBarPolicy(x, Qt.ScrollBarAlwaysOff) self.view.loadFinished.connect(self.load_finished) self.paged_js = compiled_coffeescript('ebooks.oeb.display.utils') self.paged_js += compiled_coffeescript('ebooks.oeb.display.paged') def load_finished(self, ok): self.loaded_ok = ok def start_print(self): self.pd = QPrintDialog(self.parent()) self.pd.open(self._start_print) def _start_print(self): self.do_print(self.pd.printer()) def start_preview(self): self.pd = QPrintPreviewDialog(self.parent()) self.pd.paintRequested.connect(self.do_print) self.pd.exec_() def do_print(self, printer): painter = QPainter(printer) zoomx = printer.logicalDpiX()/self.view.logicalDpiX() zoomy = printer.logicalDpiY()/self.view.logicalDpiY() painter.scale(zoomx, zoomy) pr = printer.pageRect() self.view.page().setViewportSize(QSize(pr.width()/zoomx, pr.height()/zoomy)) evaljs = self.mf.evaluateJavaScript loop = QEventLoop(self) pagenum = 0 from_, to = printer.fromPage(), printer.toPage() first = True for path in self.iterator.spine: self.loaded_ok = None load_html(path, self.view, codec=getattr(path, 'encoding', 'utf-8'), mime_type=getattr(path, 'mime_type', None)) while self.loaded_ok is None: loop.processEvents(loop.ExcludeUserInputEvents) if not self.loaded_ok: return error_dialog(self.parent(), _('Failed to render'), _('Failed to render document %s')%path, show=True) evaljs(self.paged_js) evaljs(''' document.body.style.backgroundColor = "white"; paged_display.set_geometry(1, 0, 0, 0); paged_display.layout(); paged_display.fit_images(); ''') while True: pagenum += 1 if (pagenum >= from_ and (to == 0 or pagenum <= to)): if not first: printer.newPage() first = False self.mf.render(painter) try: nsl = int(evaljs('paged_display.next_screen_location()')) except (TypeError, ValueError): break if nsl <= 0: break evaljs('window.scrollTo(%d, 0)'%nsl) painter.end()
class Window(BaseWindow): def __init__(self): self.debug=1 self.app = QApplication(sys.argv) self.desktop= QApplication.desktop() self.web = QWebView() self.icon = QIcon(ICON) QWebSettings.setIconDatabasePath(DATA_DIR) self.web.setWindowIcon(self.icon) self.web.titleChanged.connect(self.title_changed) self.web.iconChanged.connect(self.icon_changed) self.web.page().windowCloseRequested.connect(self.close_window) self.web.page().geometryChangeRequested.connect(self.set_geometry) def show(self,window_state): if window_state == "maximized" and not self.web.isMaximized(): self.web.showNormal() self.web.showMaximized() elif window_state == "fullscreen" and not self.web.isFullScreen(): self.web.showNormal() self.web.showFullScreen() elif window_state == "normal": self.web.showNormal() else: self.web.show() def run(self): return self.app.exec_() def set_debug(self, debuglevel): self.debug=debuglevel def set_geometry(self,geom ): self.web.setGeometry(geom) def close_window(self): sys.exit() def icon_changed(self): if not self.icon.isNull(): self.web.setWindowIcon(self.icon) if not self.web.icon().isNull(): self.web.setWindowIcon(self.web.icon()) def title_changed(self, title): if title: self.web.setWindowTitle(title) def load_url(self,url): self.url=QUrl.fromEncoded(url) self.web.setUrl(self.url) def set_size(self,width, height): if width<=0: width=640 if height<=0: height=480 left=(self.desktop.width()-width)/2 top=(self.desktop.height()-height)/2 self.web.setGeometry(left,top,width,height)
class CentralWidget(QWidget): l = logging.getLogger('CentralWidget') updateWindowTitle = pyqtSignal(str) def __init__(self, parentWidget, settings): QWidget.__init__(self, parentWidget) self.settings = settings self.theLayout = QHBoxLayout() self.splitter = QSplitter(self) self.theLayout.addWidget(self.splitter) self.browserWidget = BrowserWidget(self, self.settings) self.browserWidget.itemSelected.connect(self.itemSelected) self.tabWidget = QTabWidget(self) tab = QWidget() tabLayout = QVBoxLayout(tab) tab.setLayout(tabLayout) self.editorWidget = EditorWidget(tab) self.editorWidget.setObjectName("EditorWidget1") self.editorWidget.message.connect(self.showMessage) self.editorWidget.titleChanged.connect(self.updateWindowTitle) self.editorWidget.navigate.connect(self.navigate) tabLayout.addWidget(self.editorWidget) self.editTabIdx = self.tabWidget.addTab(tab, "Edit") ############################# self.browser = QWebView(self.tabWidget) self.browser.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.browser.linkClicked.connect(self.navigateWeb) self.webTabIdx = self.tabWidget.addTab(self.browser, "View web") self.pdfTabIdx = self.tabWidget.addTab(QWidget(self.tabWidget), "View pdf") self.textView = QTextEdit(self.tabWidget) self.textView.setReadOnly(True) self.textView.setFontFamily('Courier') self.textView.setFontPointSize(8) self.structureTabIdx = self.tabWidget.addTab(self.textView, "View document structure") self.customView = QTextEdit(self.tabWidget) self.customView.setReadOnly(True) self.customView.setFontFamily('Courier') self.customView.setFontPointSize(10) self.xmlTabIdx = self.tabWidget.addTab(self.customView, "View XML") self.htmlView = QTextEdit(self.tabWidget) self.htmlView.setReadOnly(True) self.htmlView.setFontFamily('Courier') self.htmlView.setFontPointSize(10) self.htmlTabIdx = self.tabWidget.addTab(self.htmlView, "View Html") self.tabWidget.currentChanged.connect(self.tabSelected) # Search/Links widget in lower left corner #################################### self.searchWidget = SearchWidget(self) self.searchWidget.resultSelected.connect(self.navigateDirect) self.toLinksWidget = LinklistWidget(self) self.toLinksWidget.resultSelected.connect(self.navigateDirect) self.fromLinksWidget = LinklistWidget(self) self.fromLinksWidget.resultSelected.connect(self.navigateDirect) self.listsWidget = QTabWidget(self) self.listsWidget.addTab(self.searchWidget, 'Search') self.listsWidget.addTab(self.toLinksWidget, 'Links to') self.listsWidget.addTab(self.fromLinksWidget, 'Links from') ############################################################################### leftWidget = QSplitter(Qt.Vertical, self) leftWidget.addWidget(self.browserWidget) leftWidget.addWidget(self.listsWidget) self.splitter.addWidget(leftWidget) self.splitter.addWidget(self.tabWidget) leftWidget.setSizes([400, 100]) # TODO self.setLayout(self.theLayout) self.splitter.setSizes([100, 400]) # TODO # self.splitter.setChildrenCollapsible(False) self.editorWidget.setEnabled(False) def navigateWeb(self, url): if url.scheme() == 'file': pageId = url.fileName() self.navigate(pageId) self.tabSelected(1) elif url.scheme() == 'http' or url.scheme() == 'https': self.browser.setUrl(url) def showMessage(self, message): self.parent().statusBar.showMessage(message, 3000) def navigate(self, pageId): """Assumption: pageId is sub page of current page""" self.l.debug('Navigating to sub page "{}"'.format(pageId)) self.editorWidget.save() self.browserWidget.navigate(pageId) # Will implicitly load the page def navigateDirect(self, pageId): """Assumption: pageId is NOT sub page of current page. Hence we need to let the browser point to the first occurrence of the page.""" self.l.debug('Navigating directly to "{}"'.format(pageId)) self.editorWidget.save() self.browserWidget.navigateDirect(pageId) # Will implicitly load the page def itemSelected(self): treeNode = self.browserWidget.currentItem self.l.debug('Selected tree node: {}'.format(treeNode)) pageId = treeNode.getPageId() notepad = treeNode.getNotepad() self.editorWidget.setEnabled(True) self.editorWidget.save() self.editorWidget.load(notepad, pageId) self.updateLinkLists(notepad, pageId) def updateLinkLists(self, notepad, pageId): linksTo = notepad.getChildPages(pageId) linksFrom = notepad.getParentPages(pageId) self.toLinksWidget.setContents(linksTo) self.fromLinksWidget.setContents(linksFrom) def tabSelected(self, index): if index == self.editTabIdx: pass elif index == self.webTabIdx: self.activateWebView() elif index == self.pdfTabIdx: pass elif index == self.structureTabIdx: self.activateStructureView() elif index == self.xmlTabIdx: self.activateXMLView() elif index == self.htmlTabIdx: self.activateHTMLView() def activateWebView(self): exporter = HTMLExporter(self.editorWidget.page.getPageDir()) self.htmlView.setPlainText(exporter.getHtmlString(self.editorWidget.editView.document())) ########### get URL for the stylesheet and for the base URL webpageCSS = pkg_resources.resource_string(data.__name__, 'webpage.css') print("webpage.css file: {} - {}".format(webpageCSS, type(webpageCSS))) mypath = os.getcwd() mypath = mypath.replace('\\', '/') baseURL = QUrl('file:///{}/'.format(mypath)) # The location must be either a path on the local filesystem, or a # data URL with UTF-8 and Base64 encoded data, such as: # "data:text/css;charset=utf-8;base64,cCB7IGJhY2tncm91bmQtY29sb3I6IHJlZCB9Ow==" # BASE64 works on bytes!! import base64 cssData = base64.b64encode(webpageCSS) cssData = cssData.decode('utf-8') cssDataUrl = 'data:text/css;charset=utf-8;base64,{}'.format(cssData) print("webpage.css base64: {}".format(cssDataUrl )) # cssDataUrl = QUrl('data:text/css;charset=utf-8;base64,{}'.format(cssData)) # 'file:///{}/webpage.css'.format(mypath)) ########### self.browser.settings().setUserStyleSheetUrl(QUrl(cssDataUrl)) self.browser.setHtml(self.htmlView.toPlainText(), baseURL) def activateStructureView(self): self.textView.clear() doc = self.editorWidget.editView.document() traversal = TextDocumentTraversal() tree = traversal.traverse(doc) sp = StructurePrinter(tree, self.textView.insertPlainText) sp.traverse() def activateXMLView(self): exporter = XMLExporter(self.editorWidget.page.getPageDir(), None) self.customView.setPlainText(exporter.getXmlString(self.editorWidget.editView.document())) def activateHTMLView(self): exporter = HTMLExporter(self.editorWidget.page.getPageDir()) self.htmlView.setPlainText(exporter.getHtmlString(self.editorWidget.editView.document()))