class HelpWindow(QtWidgets.QWidget): # TODO: do not hardcode this path like this. __doc_path = os.path.abspath( os.path.join( os.path.abspath(os.path.dirname(__file__)), "..", "..", "doc", "index.html")) def __init__(self, parent=None): super().__init__(parent) # The help display. self.help_display = QWebView(parent=self) self.help_display.load(QtCore.QUrl("file://" + self.__doc_path)) # Close button. self.close_button = QtWidgets.QPushButton("Close") self.close_button.clicked.connect(self.close) # Layout for the widgets. layout = QtWidgets.QVBoxLayout() layout.addWidget(self.help_display) layout.addWidget(self.close_button) self.setLayout(layout) # Title self.setWindowTitle("{} help".format(version.progname))
def __init__(self, vertical, parent=None): QWebView.__init__(self, parent) s = self.settings() s.setAttribute(s.JavascriptEnabled, False) self.vertical = vertical self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks) self.linkClicked.connect(self.link_activated) self._link_clicked = False self.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.palette() self.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.page().setPalette(palette) for x, icon in [ ('remove_format', 'trash.png'), ('save_format', 'save.png'), ('restore_format', 'edit-undo.png'), ('copy_link','edit-copy.png'), ('compare_format', 'diff.png'), ('set_cover_format', 'default_cover.png'), ]: ac = QAction(QIcon(I(icon)), '', self) ac.current_fmt = None ac.current_url = None ac.triggered.connect(getattr(self, '%s_triggerred'%x)) setattr(self, '%s_action'%x, ac) self.manage_action = QAction(self) self.manage_action.current_fmt = self.manage_action.current_url = None self.manage_action.triggered.connect(self.manage_action_triggered) self.remove_item_action = ac = QAction(QIcon(I('minus.png')), '...', self) ac.data = (None, None, None) ac.triggered.connect(self.remove_item_triggered) self.setFocusPolicy(Qt.NoFocus)
def PdfConverter(username): htmllink = "bootstrap_mod/usertemp/"+username+".html" app1 = QApplication(sys.argv) web = QWebView() link =QUrl.fromLocalFile(QFileInfo(htmllink).absoluteFilePath()) web.load(QUrl(link)) printer = QPrinter() printer.setPageSize(QPrinter.A4) printer.setOutputFormat(QPrinter.PdfFormat) Pdf_Generated_Name="bootstrap_mod/usertemp/"+username+".pdf" printer.setOutputFileName(Pdf_Generated_Name) web.print(printer) QApplication.exit() def convertIt(): web.print(printer) print("Pdf generated") QApplication.exit() web.loadFinished.connect(convertIt) sys.exit(app1.exec_()) return 0
def __init__(self, *args): QWebView.__init__(self, *args) self.gui = None self.tags = '' self.create_browser = None self._page = NPWebPage() self.setPage(self._page) self.cookie_jar = QNetworkCookieJar() self.page().networkAccessManager().setCookieJar(self.cookie_jar) http_proxy = get_proxies().get('http', None) if http_proxy: proxy_parts = urlparse(http_proxy) proxy = QNetworkProxy() proxy.setType(QNetworkProxy.HttpProxy) if proxy_parts.username: proxy.setUser(proxy_parts.username) if proxy_parts.password: proxy.setPassword(proxy_parts.password) if proxy_parts.hostname: proxy.setHostName(proxy_parts.hostname) if proxy_parts.port: proxy.setPort(proxy_parts.port) self.page().networkAccessManager().setProxy(proxy) self.page().setForwardUnsupportedContent(True) self.page().unsupportedContent.connect(self.start_download) self.page().downloadRequested.connect(self.start_download) self.page().networkAccessManager().sslErrors.connect(self.ignore_ssl_errors)
def __init__(self, editBox, editorPositionToSourceLineFunc, sourceLineToEditorPositionFunc): QWebView.__init__(self) self.editBox = editBox if not globalSettings.handleWebLinks: self.page().setLinkDelegationPolicy(QWebPage.DelegateExternalLinks) self.page().linkClicked.connect(QDesktopServices.openUrl) self.settings().setAttribute(QWebSettings.LocalContentCanAccessFileUrls, False) self.settings().setDefaultTextEncoding('utf-8') # Avoid caching of CSS self.settings().setObjectCacheCapacities(0,0,0) self.syncscroll = SyncScroll(self.page().mainFrame(), editorPositionToSourceLineFunc, sourceLineToEditorPositionFunc) # Events relevant to sync scrolling self.editBox.cursorPositionChanged.connect(self._handleCursorPositionChanged) self.editBox.verticalScrollBar().valueChanged.connect(self.syncscroll.handleEditorScrolled) self.editBox.resized.connect(self._handleEditorResized) # Scroll the preview when the mouse wheel is used to scroll # beyond the beginning/end of the editor self.editBox.scrollLimitReached.connect(self._handleWheelEvent)
def __init__(self, vertical, parent=None): QWebView.__init__(self, parent) s = self.settings() s.setAttribute(s.JavascriptEnabled, False) self.vertical = vertical self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks) self.linkClicked.connect(self.link_activated) self._link_clicked = False self.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.palette() self.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.page().setPalette(palette) self.css = P("templates/book_details.css", data=True).decode("utf-8") for x, icon in [ ("remove_format", "trash.png"), ("save_format", "save.png"), ("restore_format", "edit-undo.png"), ("copy_link", "edit-copy.png"), ("manage_author", "user_profile.png"), ("compare_format", "diff.png"), ]: ac = QAction(QIcon(I(icon)), "", self) ac.current_fmt = None ac.current_url = None ac.triggered.connect(getattr(self, "%s_triggerred" % x)) setattr(self, "%s_action" % x, ac)
def __init__(self, parent=None): QWebView.__init__(self, parent) self.inspector = QWebInspector(self) w = QApplication.instance().desktop().availableGeometry(self).width() self._size_hint = QSize(int(w/3), int(w/2)) self._page = WebPage(self) self.setPage(self._page) self.inspector.setPage(self._page) self.clear()
class BrowserView(QDialog): # {{{ def __init__(self, page, parent=None): QDialog.__init__(self, parent) self.l = l = QVBoxLayout(self) self.setLayout(l) self.webview = QWebView(self) l.addWidget(self.webview) self.resize(QSize(1024, 768)) self.webview.setPage(page)
def __init__(self, url): self.app = QApplication(sys.argv) QWebView.__init__(self) self.setPage(SilentWebPage()) self._loaded = False # Used within _wait_load. self.loadFinished.connect(self._loadFinished) # The version of webkit I have prints some stuff to stdout which I do not want. # See https://bugreports.qt-project.org/browse/QTBUG-31074 with suppress_stdout_stderr(): self.load(QUrl(url)) #self.show() self._wait_load()
def js_tester(qtbot): """Fixture to test javascript snippets. Provides a QWebView with a 640x480px size and a JSTester instance. Args: qtbot: pytestqt.plugin.QtBot fixture. """ webview = QWebView() qtbot.add_widget(webview) webview.resize(640, 480) return JSTester(webview, qtbot)
def __init__(self, parent): QWebView.__init__(self, parent) self._page = Page() self._page.elem_clicked.connect(self.elem_clicked) self.setPage(self._page) raw = ''' body { background-color: white } .calibre_toc_hover:hover { cursor: pointer !important; border-top: solid 5px green !important } ''' raw = '::selection {background:#ffff00; color:#000;}\n'+raw data = 'data:text/css;charset=utf-8;base64,' data += b64encode(raw.encode('utf-8')) self.settings().setUserStyleSheetUrl(QUrl(data))
def __init__(self, parent=None): QWebView.__init__(self, parent) self.setAcceptDrops(False) self.setMaximumWidth(300) self.setMinimumWidth(300) palette = self.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.page().setPalette(palette) self.setAttribute(Qt.WA_OpaquePaintEvent, False) self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks) self.linkClicked.connect(self.link_clicked)
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()
def __init__(self): QWebView.__init__(self) settings = self.settings() settings.setAttribute(settings.AutoLoadImages, False) self.authorize = False self.feed_list = [] self.page_loaded = False self.scroll_down = False self.page_height = 0 self.run_timer = True self.trg_func = self.cap self.textpage = None self.loadFinished.connect(self.set_textpage)
def __init__(self, url): super(MainWindow, self).__init__() self.view = QWebView(self) self.view.load(url) self.view.setFixedSize(890, 550) # comment out the following line to allow refresh for debugging self.view.setContextMenuPolicy(Qt.NoContextMenu)
def __init__(self): super(WebRender, self).__init__() vbox, temporary_directory = QVBoxLayout(self), mkdtemp() # Web Frame self.webFrame = QWebView() # QWebView = QWebFrame + QWebSettings self.webFrame.setStyleSheet("QWebView{ background:#fff }") # no dark bg settings = self.webFrame.settings() # QWebSettings instance settings.setDefaultTextEncoding("utf-8") settings.setIconDatabasePath(temporary_directory) settings.setLocalStoragePath(temporary_directory) settings.setOfflineStoragePath(temporary_directory) settings.setOfflineWebApplicationCachePath(temporary_directory) settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) settings.setAttribute(QWebSettings.OfflineStorageDatabaseEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, True) settings.setAttribute(QWebSettings.DnsPrefetchEnabled, True) settings.setAttribute(QWebSettings.JavascriptCanOpenWindows, True) settings.setAttribute(QWebSettings.JavascriptCanCloseWindows, True) settings.setAttribute(QWebSettings.JavascriptCanAccessClipboard, True) settings.setAttribute(QWebSettings.SpatialNavigationEnabled, True) settings.setAttribute( QWebSettings.LocalContentCanAccessRemoteUrls, True) settings.setAttribute( QWebSettings.OfflineWebApplicationCacheEnabled, True) vbox.addWidget(self.webFrame)
def initUI(self): self.setWindowIcon(QIcon("image/tray.png")) self.setWindowTitle("梦音乐") self.setWindowFlags(Qt.FramelessWindowHint) self.setObjectName("window") self.setGeometry(300,0,600, 600) # 底部 ql = QLabel(self) ql.setGeometry(0,580,600,20) ql.setStyleSheet("QLabel{ background:#2D2D2D }") # 内嵌web网页 self.web = QWebView(self) self.web.setStyleSheet("QWidget{ background-color:white }") self.web.setGeometry(0, 0, 600, 580) self.web.load(QUrl.fromLocalFile(os.path.abspath("web/index.html"))) # self.web.load(QUrl("http://localhost/index.php/Home/Index/index.html")) self.web.page().mainFrame().javaScriptWindowObjectCleared.connect( self.populateJavaScriptWindowObject) # 关闭按钮 btn = QPushButton("", self) btn.setGeometry(540, 5, 44, 34) # btn.setCursor(QCursor(Qt.PointingHandCursor)) btn.setStyleSheet( "QPushButton{ border-image:url(image/newimg/ic_common_title_bar_back_2.png) } QPushButton:hover{ border-image:url(image/newimg/ic_common_title_bar_back.png) } ") btn.clicked.connect(self.myclose)
def cb_initialize_plugin(self): # --------------------------- # Read configuration # --------------------------- self.config = self.pl_get_current_config_ref() content = self.config['content']['value'] isUrl = self.config['isUrl']['value'] # -------------------------------- # Create Widget # -------------------------------- self.WebView = QWebView() # This call is important, because the background structure needs to know the used widget! # In the background the qmidiwindow will becreated and the widget will be added self.pl_set_widget_for_internal_usage( self.WebView ) print(isUrl) if isUrl == '1': url = QtCore.QUrl(content) self.WebView.load(url) else: self.WebView.setHtml(content) self.WebView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.WebView.customContextMenuRequested.connect(self.show_context_menu) return True
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 __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 __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 __init__(self, container, do_embed=False): self.container = container self.log = self.logger = container.log self.do_embed = do_embed must_use_qt() self.parser = CSSParser(loglevel=logging.CRITICAL, log=logging.getLogger('calibre.css')) self.first_letter_pat = regex.compile(r'^[\p{Ps}\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]+', regex.VERSION1 | regex.UNICODE) self.loop = QEventLoop() self.view = QWebView() self.page = Page(self.log) self.view.setPage(self.page) self.page.setViewportSize(QSize(1200, 1600)) self.view.loadFinished.connect(self.collect, type=Qt.QueuedConnection) self.render_queue = list(container.spine_items) self.font_stats = {} self.font_usage_map = {} self.font_spec_map = {} self.font_rule_map = {} self.all_font_rules = {} QTimer.singleShot(0, self.render_book) if self.loop.exec_() == 1: raise Exception('Failed to gather statistics from book, see log for details')
def __init__(self, book_mi, annotations, parent=None): #QDialog.__init__(self, parent) self.prefs = plugin_prefs super(PreviewDialog, self).__init__(parent, 'annotations_preview_dialog') self.pl = QVBoxLayout(self) self.setLayout(self.pl) self.label = QLabel() self.label.setText("<b>%s annotations · %s</b>" % (book_mi.reader_app, book_mi.title)) self.label.setAlignment(Qt.AlignHCenter) self.pl.addWidget(self.label) self.wv = QWebView() self.wv.setHtml(annotations) self.pl.addWidget(self.wv) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Close) # self.buttonbox.addButton('Close', QDialogButtonBox.AcceptRole) self.buttonbox.setOrientation(Qt.Horizontal) # self.buttonbox.accepted.connect(self.close) self.buttonbox.rejected.connect(self.close) # self.connect(self.buttonbox, pyqtSignal('accepted()'), self.close) # self.connect(self.buttonbox, pyqtSignal('rejected()'), self.close) self.pl.addWidget(self.buttonbox) # Sizing sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.resize_dialog()
def __init__(self, parent, icon, prefs, html=None, page=None, title=''): self.prefs = prefs #QDialog.__init__(self, parent=parent) super(HelpView, self).__init__(parent, 'help_dialog') self.setWindowTitle(title) self.setWindowIcon(icon) self.l = QVBoxLayout(self) self.setLayout(self.l) self.wv = QWebView() if html is not None: self.wv.setHtml(html) elif page is not None: self.wv.load(QUrl(page)) self.wv.setMinimumHeight(100) self.wv.setMaximumHeight(16777215) self.wv.setMinimumWidth(400) self.wv.setMaximumWidth(16777215) self.wv.setGeometry(0, 0, 400, 100) self.wv.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.l.addWidget(self.wv) # Sizing sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.resize_dialog()
def __init__(self): self.debug = 1 self.app = QApplication([]) self.desktop = QApplication.desktop() self.web = QWebView() self.icon = QIcon(ICON) QWebSettings.setIconDatabasePath(DATA_DIR)
def __init__(self, tab, editorPositionToSourceLineFunc, sourceLineToEditorPositionFunc): QWebView.__init__(self) self.tab = tab self.syncscroll = SyncScroll(self.page().mainFrame(), editorPositionToSourceLineFunc, sourceLineToEditorPositionFunc) ReTextWebPreview.__init__(self, tab.editBox) self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.page().linkClicked.connect(self._handleLinkClicked) self.settings().setAttribute(QWebSettings.LocalContentCanAccessFileUrls, False) # Avoid caching of CSS self.settings().setObjectCacheCapacities(0,0,0)
def __init__(self, callback, xpos, ypos, width, height): super(Window, self).__init__() self._xpos = xpos self._ypos = ypos self._width = width self._height = height self._callback = callback self.setStyleSheet( "QWidget { background-color : #1B1D1E; color : #FFFFFF; }") self.setAsBar() view = QWebView(self) view.setContextMenuPolicy(Qt.CustomContextMenu) view.statusBarMessage.connect(self.qt_statusbar_callback) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(view) view.setPage(QWebPage()) view.setHtml(htmltemplate(self._width, self._height, "bottom" if ypos == 0 else "top")) self.view = view self.updateContent.connect(self.handle_updateContent)
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
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 __init__(self): super(WebRender, self).__init__() vbox = QVBoxLayout(self) #Web Frame self.webFrame = QWebView() QWebSettings.globalSettings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) vbox.addWidget(self.webFrame)
def __init__(self): # QWebView self.view = QWebView.__init__(self) # self.view.setPage(MyBrowser()) self.setWindowTitle('Loading...') self.titleChanged.connect(self.adjustTitle)
class StatsCollector(object): def __init__(self, container, do_embed=False): self.container = container self.log = self.logger = container.log self.do_embed = do_embed must_use_qt() self.parser = CSSParser(loglevel=logging.CRITICAL, log=logging.getLogger('calibre.css')) self.first_letter_pat = regex.compile( r'^[\p{Ps}\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]+', regex.VERSION1 | regex.UNICODE) self.loop = QEventLoop() self.view = QWebView() self.page = Page(self.log) self.view.setPage(self.page) self.page.setViewportSize(QSize(1200, 1600)) self.view.loadFinished.connect(self.collect, type=Qt.QueuedConnection) self.render_queue = list(container.spine_items) self.font_stats = {} self.font_usage_map = {} self.font_spec_map = {} self.font_rule_map = {} self.all_font_rules = {} QTimer.singleShot(0, self.render_book) if self.loop.exec_() == 1: raise Exception( 'Failed to gather statistics from book, see log for details') def log_exception(self, *args): orig = self.log.filter_level try: self.log.filter_level = self.log.DEBUG self.log.exception(*args) finally: self.log.filter_level = orig def render_book(self): try: if not self.render_queue: self.loop.exit() else: self.render_next() except: self.log_exception('Rendering failed') self.loop.exit(1) def render_next(self): item = unicode(self.render_queue.pop(0)) self.current_item = item load_html(item, self.view) def collect(self, ok): if not ok: self.log.error('Failed to render document: %s' % self.container.relpath(self.current_item)) self.loop.exit(1) return try: self.page.load_js() self.collect_font_stats() except: self.log_exception('Failed to collect font stats from: %s' % self.container.relpath(self.current_item)) self.loop.exit(1) return self.render_book() def href_to_name(self, href, warn_name): if not href.startswith('file://'): self.log.warn('Non-local URI in', warn_name, ':', href, 'ignoring') return None src = href[len('file://'):] if iswindows and len(src) > 2 and (src[0], src[2]) == ('/', ':'): src = src[1:] src = src.replace('/', os.sep) src = unquote(src) name = self.container.abspath_to_name(src) if not self.container.has_name(name): self.log.warn('Missing resource', href, 'in', warn_name, 'ignoring') return None return name def collect_font_stats(self): self.page.evaljs('window.font_stats.get_font_face_rules()') font_face_rules = self.page.bridge_value if not isinstance(font_face_rules, list): raise Exception( 'Unknown error occurred while reading font-face rules') # Weed out invalid font-face rules rules = [] import tinycss parser = tinycss.make_full_parser() for rule in font_face_rules: ff = rule.get('font-family', None) if not ff: continue style = self.parser.parseStyle('font-family:%s' % ff, validate=False) ff = [ x.value for x in style.getProperty('font-family').propertyValue ] if not ff or ff[0] == 'inherit': continue rule['font-family'] = frozenset(icu_lower(f) for f in ff) src = rule.get('src', None) if not src: continue try: tokens = parser.parse_stylesheet( '@font-face { src: %s }' % src).rules[0].declarations[0].value except Exception: self.log.warn('Failed to parse @font-family src: %s' % src) continue for token in tokens: if token.type == 'URI': uv = token.value if uv: sn = self.href_to_name(uv, '@font-face rule') if sn is not None: rule['src'] = sn break else: self.log.warn( 'The @font-face rule refers to a font file that does not exist in the book: %s' % src) continue normalize_font_properties(rule) rule['width'] = widths[rule['font-stretch']] rule['weight'] = int(rule['font-weight']) rules.append(rule) if not rules and not self.do_embed: return self.font_rule_map[self.container.abspath_to_name( self.current_item)] = rules for rule in rules: self.all_font_rules[rule['src']] = rule for rule in rules: if rule['src'] not in self.font_stats: self.font_stats[rule['src']] = set() self.page.evaljs('window.font_stats.get_font_usage()') font_usage = self.page.bridge_value if not isinstance(font_usage, list): raise Exception('Unknown error occurred while reading font usage') self.page.evaljs('window.font_stats.get_pseudo_element_font_usage()') pseudo_element_font_usage = self.page.bridge_value if not isinstance(pseudo_element_font_usage, list): raise Exception( 'Unknown error occurred while reading pseudo element font usage' ) font_usage += get_pseudo_element_font_usage(pseudo_element_font_usage, self.first_letter_pat, self.parser) exclude = {'\n', '\r', '\t'} self.font_usage_map[self.container.abspath_to_name( self.current_item)] = fu = defaultdict(dict) bad_fonts = { 'serif', 'sans-serif', 'monospace', 'cursive', 'fantasy', 'sansserif', 'inherit' } for font in font_usage: text = set() for t in font['text']: text |= frozenset(t) text.difference_update(exclude) if not text: continue normalize_font_properties(font) for rule in get_matching_rules(rules, font): self.font_stats[rule['src']] |= text if self.do_embed: ff = [icu_lower(x) for x in font.get('font-family', [])] if ff and ff[0] not in bad_fonts: keys = { 'font-weight', 'font-style', 'font-stretch', 'font-family' } key = frozenset(((k, ff[0] if k == 'font-family' else v) for k, v in font.iteritems() if k in keys)) val = fu[key] if not val: val.update({ k: (font[k][0] if k == 'font-family' else font[k]) for k in keys }) val['text'] = set() val['text'] |= text self.font_usage_map[self.container.abspath_to_name( self.current_item)] = dict(fu) if self.do_embed: self.page.evaljs('window.font_stats.get_font_families()') font_families = self.page.bridge_value if not isinstance(font_families, dict): raise Exception( 'Unknown error occurred while reading font families') self.font_spec_map[self.container.abspath_to_name( self.current_item)] = fs = set() for font_dict, text, pseudo in pseudo_element_font_usage: font_families[font_dict['font-family']] = True for raw in font_families.iterkeys(): for x in parse_font_families(self.parser, raw): if x.lower() not in bad_fonts: fs.add(x)
class PageCoordinator(QObject): marker = '//!>' schedule_key = 'schedule' job_recalculate_minutes = 15 job_limit = 100 def __init__(self, instances, parent=None, debug_file=None, queue_size=1000): super().__init__(parent) if debug_file: # When in debugging mode we only load and single instance and show it to the user self.instances = 1 else: self.instances = instances self.web_pages = [] self.job_list = [] self.job_queue = Queue(maxsize=queue_size) for _ in range(self.instances): wp = WebPageCustom(self) wp.job_finished.connect(self.distribute_jobs) wp.new_job_received.connect(self.queue_new_job) self.web_pages.append(wp) if debug_file: # When in debugging mode we only load and single instance and show it to the user custom_webpage = self.web_pages[0] self.main_window = QMainWindow() self.web_view = QWebView(self.main_window) self.web_view.setPage(custom_webpage) self.main_window.setCentralWidget(self.web_view) self.main_window.showFullScreen() self.main_window.setWindowTitle("SpiderJuice Debug Window") self.main_window.show() self.queue_new_job(Job(file=debug_file)) else: self.parse_local_jobs() self.recalculate_timer = QTimer(self) self.recalculate_timer.timeout.connect(self.shedule_for_next_15_min) self.recalculate_timer.setTimerType(Qt.VeryCoarseTimer) self.recalculate_timer.start(self.job_recalculate_minutes * 60 * 1000) self.shedule_for_next_15_min() def parse_local_jobs(self): logger.info('Parsing Local Jobs') for file in glob.glob("{base}/jobs/*.js".format(base=BASE_PROJECT_DIR)): with open(file, encoding='utf-8', mode='r') as job_file: job_conf = {} while True: line = job_file.readline().strip() if not line.startswith(self.marker): break line = line.split(self.marker, 1)[1] if ':' not in line: break key, value = line.split(':', 1) job_conf[key.strip()] = value.strip() if self.schedule_key in job_conf: job_conf['file'] = file self.job_list.append(Job(**job_conf)) @pyqtSlot(Job) def queue_new_job(self, job): try: self.job_queue.put(job, block=False) self.distribute_jobs() except queue.Full as e: logger.exception('The queue is full. Ignored Job {}, {}'.format(job, e)) @pyqtSlot() def distribute_jobs(self): self.check_no_work() if self.job_queue.empty(): return for web_page in self.web_pages: if self.job_queue.empty(): return if not web_page.is_busy(): job = self.job_queue.get(block=False) web_page.load_job(job) def check_no_work(self): if not self.job_queue.empty(): return for web_page in self.web_pages: if web_page.is_busy(): return # Means that nothing is running proc = psutil.Process(os.getpid()) logger.info('Running Garbage Collector. {}: {}'.format(proc.memory_percent(), proc.memory_info())) gc.collect() logger.info('After Garbage Collector. {}: {}'.format(proc.memory_percent(), proc.memory_info())) @pyqtSlot() def shedule_for_next_15_min(self): now = datetime.now() for job in self.job_list: if job.schedule == 'once': self.queue_new_job(job) continue cron_iter = croniter(job.schedule, datetime.now()) for _ in range(self.job_limit): next_job_time = cron_iter.get_next(datetime) second_till_next_job = (next_job_time - now).total_seconds() job_recalculate_seconds = self.job_recalculate_minutes * 60 if second_till_next_job <= job_recalculate_seconds: milliseconds = second_till_next_job * 1000 QTimer.singleShot(milliseconds, Qt.VeryCoarseTimer, lambda: self.queue_new_job(job)) else: break @pyqtSlot(dict) def add_job_to_queue(self, data): """Safe to call from another thread""" logger.debug('Recieved data: {}'.format(data))
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 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 = '' 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, 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 = 36 if self.footer and opts.margin_bottom < min_margin: self.log.warn( 'Bottom margin is too small for footer, increasing it.') 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.') 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): sections = defaultdict(list) ci = os.path.abspath(os.path.normcase(self.current_item)) if self.toc is not None: for toc in self.toc.flat(): 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']) 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() while True: if col in sections: self.current_section = sections[col][0] elif col - 1 in sections: # Ensure we are using the last section on the previous page as # the section for this page, since this page has no sections self.current_section = sections[col - 1][-1] 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() 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'])
subprocess.Popen(shlex.split(relaunch_cmd), stderr=None, stdout=None) sys.exit() except IOError: continue if __name__ == "__main__": order_num = sys.argv[-1] collector_log = util.parserLog( '/var/log/sbp/flashscore/collector_statistics.log', 'flashscore-collector-statistics') app = QApplication(sys.argv) web = QWebView() webpage = Collector(parent=web, page_link=common.live_link, debug=True, logger=collector_log, order_num=order_num) web.setPage(webpage) web.setGeometry(780, 0, 1200, 768) web.show() try: sys.exit(app.exec_()) except Exception as e: collector_log.critical('APP: {}'.format(e))
class App(QMainWindow): @property def QSETTINGS(self): return QSettings(QSettings.IniFormat, QSettings.UserScope, "mdviewer", "session") def __init__(self, parent=None, filename=""): QMainWindow.__init__(self, parent) self.filename = filename or os.path.join(app_dir, u"README.md") # Set environment variables self.set_env() # Configure Preview window self.set_window_title() self.resize(self.QSETTINGS.value("size", QSize(800, 400))) self.move(self.QSETTINGS.value("pos", QPoint(0, 0))) # Activate WebView self.web_view = QWebView() self.setCentralWidget(self.web_view) self.scroll_pos = {} # Configure and start file watcher thread self.thread1 = WatcherThread(self.filename) self.thread1.update.connect(self.update) self.watcher = QFileSystemWatcher([self.filename]) self.watcher.fileChanged.connect(self.thread1.run) self.thread1.run() self.web_view.loadFinished.connect(self.after_update) # Get style sheet self.stylesheet = self.QSETTINGS.value("stylesheet", "default.css") # Set GUI menus and toolbars self.set_menus() self.set_search_bar() def set_env(self): path, name = os.path.split(os.path.abspath(self.filename)) ext = os.path.splitext(name)[-1].lower() os.environ["MDVIEWER_EXT"] = ext[1:] os.environ["MDVIEWER_FILE"] = name os.environ["MDVIEWER_ORIGIN"] = path def set_window_title(self): _path, name = os.path.split(os.path.abspath(self.filename)) self.setWindowTitle(u"%s – MDviewer" % (name)) def update(self, text, warn): "Update Preview." self.web_view.settings().setAttribute(QWebSettings.JavascriptEnabled, True) self.web_view.settings().setAttribute(QWebSettings.PluginsEnabled, True) self.web_view.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) # Configure link behavior self.web_view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.web_view.linkClicked.connect( lambda url: self.handle_link_clicked(url)) # Save scroll position if not self.web_view.page().currentFrame().scrollPosition() == QPoint( 0, 0): self.scroll_pos[self.filename] = self.web_view.page().currentFrame( ).scrollPosition() # Update Preview self.web_view.setHtml(text, baseUrl=QUrl.fromLocalFile( os.path.join(os.getcwd(), self.filename))) # Load JavaScript and core CSS scr = os.path.join(app_dir, "mdviewer.js") css = os.path.join(app_dir, "mdviewer.css") add_resources = """ (function () { var head = document.querySelector("head"); var css = document.createElement("link"); css.rel = "stylesheet"; css.href = "%s"; css.id = "coreCSS"; head.appendChild(css); var script = document.createElement("script"); script.type = "text/javascript"; script.src = "%s"; script.id = "coreJS"; script.setAttribute("defer", ""); head.appendChild(script); })() """ % (css, scr) self.web_view.page().currentFrame().evaluateJavaScript(add_resources) # Display processor warnings if warn: QMessageBox.warning(self, "Processor message", warn) def after_update(self): "Restore scroll position." try: pos = self.scroll_pos[self.filename] except KeyError: pass else: self.web_view.page().currentFrame().evaluateJavaScript( "window.scrollTo(%s, %s)" % (pos.x(), pos.y())) def open_file(self): filename, _filter = QFileDialog.getOpenFileName( self, "Open File", os.path.dirname(self.filename)) if filename != "": self.filename = self.thread1.filename = filename self.set_env() self.set_window_title() self.thread1.run() else: pass def save_html(self): filename, _filter = QFileDialog.getSaveFileName( self, "Save File", os.path.dirname(self.filename)) if filename != "": proc = Settings.get("processor_path", "pandoc") args = Settings.get("processor_args", "") args = ("%s" % (args)).split() + [self.filename] caller = QProcess() caller.start(proc, args) caller.waitForFinished() html = str(caller.readAllStandardOutput(), "utf8") with io.open(filename, "w", encoding="utf8") as f: f.writelines(html) f.close() else: pass def find(self, text, btn=""): page = self.web_view.page() back = page.FindFlags(1) if btn is self.prev else page.FindFlags(0) case = page.FindFlags(2) if self.case.isChecked() else page.FindFlags( 0) wrap = page.FindFlags(4) if self.wrap.isChecked() else page.FindFlags( 0) page.findText("", page.FindFlags(8)) page.findText(text, back | wrap | case) def set_search_bar(self): self.search_bar = QToolBar() self.search_bar.setMovable(False) self.search_bar.setFloatable(False) self.search_bar.setVisible(False) self.search_bar.layout().setSpacing(1) self.addToolBar(0x8, self.search_bar) self.text = QLineEdit(self) self.text.setClearButtonEnabled(True) self.text.setPlaceholderText(u"Search") self.case = QCheckBox(u"Case sensitive", self) self.wrap = QCheckBox(u"Wrap", self) self.next = QPushButton(u"Next", self) self.next.setToolTip(u"Find next") self.next.setShortcut(QKeySequence("Return")) self.next.setDisabled(True) self.prev = QPushButton(u"Previous", self) self.prev.setToolTip(u"Find previous") self.prev.setShortcut(QKeySequence("Shift+Return")) self.prev.setDisabled(True) self.done = QPushButton(u"Done", self) self.done.setToolTip(u"Hide Search bar") self.done.setShortcut(QKeySequence("Esc")) def _enable_nav(): if self.text.text() == "": self.next.setDisabled(True) self.prev.setDisabled(True) else: self.next.setDisabled(False) self.prev.setDisabled(False) def _toggle_btn(btn=""): self.text.setFocus() self.find(self.text.text(), btn) def _hide(): if self.search_bar.isVisible(): self.search_bar.hide() self.search_bar.addWidget(self.done) self.search_bar.addSeparator() self.search_bar.addWidget(self.case) self.search_bar.addWidget(self.wrap) self.search_bar.addWidget(self.text) self.search_bar.addSeparator() self.search_bar.addWidget(self.next) self.search_bar.addWidget(self.prev) for btn in (self.prev, self.next): btn.pressed[()].connect(lambda btn=btn: _toggle_btn(btn)) self.done.pressed.connect(_hide) self.text.textChanged.connect(self.find) self.text.textChanged.connect(_enable_nav) def show_search_bar(self): self.search_bar.show() self.text.setFocus() self.text.selectAll() def print_doc(self): dialog = QPrintPreviewDialog() dialog.paintRequested.connect(self.web_view.print_) dialog.exec_() def quit(self, QCloseEvent): self.QSETTINGS.setValue("size", self.size()) self.QSETTINGS.setValue("pos", self.pos()) self.QSETTINGS.setValue("stylesheet", self.stylesheet) QtWidgets.qApp.quit() def zoom_in(self): self.web_view.setZoomFactor(self.web_view.zoomFactor() + .1) def zoom_out(self): self.web_view.setZoomFactor(self.web_view.zoomFactor() - .1) def zoom_reset(self): self.web_view.setZoomFactor(1) def scroll_down(self): self.web_view.page().currentFrame().scroll( 0, +self.web_view.page().viewportSize().height()) def scroll_up(self): self.web_view.page().currentFrame().scroll( 0, -self.web_view.page().viewportSize().height()) def toggle_toc(self): self.web_view.page().currentFrame().evaluateJavaScript("toggleTOC()") def handle_link_clicked(self, url): if url.isLocalFile(): if url.toLocalFile() == os.path.abspath( self.filename) and url.hasFragment(): self.web_view.page().currentFrame().scrollToAnchor( url.fragment()) return else: QDesktopServices.openUrl(url) else: QDesktopServices.openUrl(url) @staticmethod def set_stylesheet(self, stylesheet="default.css"): path = os.path.join(css_dir, stylesheet) url = QUrl.fromLocalFile(path) self.web_view.settings().setUserStyleSheetUrl(url) self.stylesheet = stylesheet def about(self): msg_about = QMessageBox(0, "About MDviewer", u"MDviewer\n\nVersion: %s" % (VERSION), parent=self) msg_about.show() def report_issue(self): url = QUrl.fromUserInput( "https://github.com/saf-dmitry/mdviewer/issues") QDesktopServices.openUrl(url) def set_menus(self): menubar = self.menuBar() file_menu = menubar.addMenu("&File") for d in ({ "name": u"&Open...", "shct": "Ctrl+O", "func": self.open_file }, { "name": u"&Save HTML...", "shct": "Ctrl+S", "func": self.save_html }, { "name": u"&Find...", "shct": "Ctrl+F", "func": self.show_search_bar }, { "name": u"&Print...", "shct": "Ctrl+P", "func": self.print_doc }, { "name": u"&Quit", "shct": "Ctrl+Q", "func": self.quit }): action = QAction(d["name"], self) action.setShortcut(QKeySequence(d["shct"])) action.triggered.connect(d["func"]) file_menu.addAction(action) view_menu = menubar.addMenu("&View") for d in ({ "name": u"Zoom &In", "shct": "Ctrl++", "func": self.zoom_in }, { "name": u"Zoom &Out", "shct": "Ctrl+-", "func": self.zoom_out }, { "name": u"&Actual Size", "shct": "Ctrl+0", "func": self.zoom_reset }): action = QAction(d["name"], self) action.setShortcut(QKeySequence(d["shct"])) action.triggered.connect(d["func"]) view_menu.addAction(action) style_menu = menubar.addMenu("&Style") style_menu.setStyleSheet("menu-scrollable: 1") style_menu.setDisabled(True) if os.path.exists(css_dir): files = sorted(os.listdir(css_dir)) files = [f for f in files if f.endswith(".css")] if len(files) > 0: style_menu.setDisabled(False) group = QActionGroup(self, exclusive=True) for i, f in enumerate(files, start=1): name = os.path.splitext(f)[0].replace("&", "&&") action = group.addAction(QtWidgets.QAction(name, self)) action.triggered.connect(lambda x, stylesheet=f: self. set_stylesheet(self, stylesheet)) if i < 10: action.setShortcut(QKeySequence("Ctrl+%d" % i)) action.setCheckable(True) style_menu.addAction(action) if f == self.stylesheet: action.trigger() help_menu = menubar.addMenu("&Help") for d in ( { "name": u"&About...", "func": self.about }, { "name": u"&Report an Issue", "func": self.report_issue }, ): action = QAction(d["name"], self) action.triggered.connect(d["func"]) help_menu.addAction(action) # Redefine reload action reload_action = self.web_view.page().action(QWebPage.Reload) reload_action.setShortcut(QKeySequence.Refresh) reload_action.triggered.connect(self.thread1.run) self.web_view.addAction(reload_action) # Define additional shortcuts QShortcut(QKeySequence("j"), self, activated=self.scroll_down) QShortcut(QKeySequence("k"), self, activated=self.scroll_up) QShortcut(QKeySequence("t"), self, activated=self.toggle_toc) def closeEvent(self, event): self.quit(event) event.accept()
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"]
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
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'])
def setUrl(self, qurl): self.page().current_root = current_container().root return QWebView.setUrl(self, qurl)
def __init__(self, parent=None): QWebView.__init__(self, parent) self.base_url = None self._parent = weakref.ref(parent) self.readonly = False self.comments_pat = re.compile(r'<!--.*?-->', re.DOTALL) extra_shortcuts = { 'ToggleBold': 'Bold', 'ToggleItalic': 'Italic', 'ToggleUnderline': 'Underline', } for wac, name, icon, text, checkable in [ ('ToggleBold', 'bold', 'format-text-bold', _('Bold'), True), ('ToggleItalic', 'italic', 'format-text-italic', _('Italic'), True), ('ToggleUnderline', 'underline', 'format-text-underline', _('Underline'), True), ('ToggleStrikethrough', 'strikethrough', 'format-text-strikethrough', _('Strikethrough'), True), ('ToggleSuperscript', 'superscript', 'format-text-superscript', _('Superscript'), True), ('ToggleSubscript', 'subscript', 'format-text-subscript', _('Subscript'), True), ('InsertOrderedList', 'ordered_list', 'format-list-ordered', _('Ordered list'), True), ('InsertUnorderedList', 'unordered_list', 'format-list-unordered', _('Unordered list'), True), ('AlignLeft', 'align_left', 'format-justify-left', _('Align left'), False), ('AlignCenter', 'align_center', 'format-justify-center', _('Align center'), False), ('AlignRight', 'align_right', 'format-justify-right', _('Align right'), False), ('AlignJustified', 'align_justified', 'format-justify-fill', _('Align justified'), False), ('Undo', 'undo', 'edit-undo', _('Undo'), False), ('Redo', 'redo', 'edit-redo', _('Redo'), False), ('RemoveFormat', 'remove_format', 'edit-clear', _('Remove formatting'), False), ('Copy', 'copy', 'edit-copy', _('Copy'), False), ('Paste', 'paste', 'edit-paste', _('Paste'), False), ('Cut', 'cut', 'edit-cut', _('Cut'), False), ('Indent', 'indent', 'format-indent-more', _('Increase indentation'), False), ('Outdent', 'outdent', 'format-indent-less', _('Decrease indentation'), False), ('SelectAll', 'select_all', 'edit-select-all', _('Select all'), False), ]: ac = PageAction(wac, icon, text, checkable, self) setattr(self, 'action_' + name, ac) ss = extra_shortcuts.get(wac, None) if ss: ac.setShortcut(QKeySequence(getattr(QKeySequence, ss))) if wac == 'RemoveFormat': ac.triggered.connect(self.remove_format_cleanup, type=Qt.QueuedConnection) self.action_color = QAction(QIcon(I('format-text-color.png')), _('Foreground color'), self) self.action_color.triggered.connect(self.foreground_color) self.action_background = QAction(QIcon(I('format-fill-color.png')), _('Background color'), self) self.action_background.triggered.connect(self.background_color) self.action_block_style = QAction(QIcon(I('format-text-heading.png')), _('Style text block'), self) self.action_block_style.setToolTip(_('Style the selected text block')) self.block_style_menu = QMenu(self) self.action_block_style.setMenu(self.block_style_menu) self.block_style_actions = [] for text, name in [ (_('Normal'), 'p'), (_('Heading') + ' 1', 'h1'), (_('Heading') + ' 2', 'h2'), (_('Heading') + ' 3', 'h3'), (_('Heading') + ' 4', 'h4'), (_('Heading') + ' 5', 'h5'), (_('Heading') + ' 6', 'h6'), (_('Pre-formatted'), 'pre'), (_('Blockquote'), 'blockquote'), (_('Address'), 'address'), ]: ac = BlockStyleAction(text, name, self) self.block_style_menu.addAction(ac) self.block_style_actions.append(ac) self.action_insert_link = QAction(QIcon(I('insert-link.png')), _('Insert link or image'), self) self.action_insert_hr = QAction(QIcon(I('format-text-hr.png')), _('Insert separator'), self) self.action_insert_link.triggered.connect(self.insert_link) self.action_insert_hr.triggered.connect(self.insert_hr) self.pageAction(QWebPage.ToggleBold).changed.connect( self.update_link_action) self.action_insert_link.setEnabled(False) self.action_insert_hr.setEnabled(False) self.action_clear = QAction(QIcon(I('trash.png')), _('Clear'), self) self.action_clear.triggered.connect(self.clear_text) self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.page().linkClicked.connect(self.link_clicked) secure_web_page(self.page().settings()) self.setHtml('') self.set_readonly(False) self.page().contentsChanged.connect(self.data_changed)
def __init__(self, parent=None): QWebView.__init__(self, parent) self.requestQueue = [] self.isProcessingExclusively = False
class HandlerClass: def __init__(self, halcomp, widgets, paths): self.h = halcomp self.w = widgets self.PATHS = paths self.gcodes = GCodes() self.valid = QtGui.QDoubleValidator(-999.999, 999.999, 3) KEYBIND.add_call('Key_F12','on_keycall_F12') KEYBIND.add_call('Key_Pause', 'on_keycall_pause') # some global variables self.run_time = 0 self.time_tenths = 0 self.timerOn = False self.home_all = False self.max_linear_velocity = INFO.MAX_LINEAR_VELOCITY * 60 self.system_list = ["G54","G55","G56","G57","G58","G59","G59.1","G59.2","G59.3"] self.slow_jog_factor = 10 self.reload_tool = 0 self.last_loaded_program = "" self.first_turnon = True self.lineedit_list = ["work_height", "touch_height", "sensor_height", "laser_x", "laser_y", "sensor_x", "sensor_y", "search_vel", "probe_vel", "max_probe", "eoffset_count"] self.onoff_list = ["frame_program", "frame_tool", "frame_dro", "frame_override", "frame_status"] self.auto_list = ["chk_eoffsets", "cmb_gcode_history"] self.axis_a_list = ["label_axis_a", "dro_axis_a", "action_zero_a", "axistoolbutton_a", "action_home_a", "widget_jog_angular", "widget_increments_angular", "a_plus_jogbutton", "a_minus_jogbutton"] self.html = """<html> <head> <title>Test page for the download:// scheme</title> </head> <body> <h1>Setup Tab</h1> <p>If you select a file with .html as a file ending, it will be shown here..</p> <img src="file://%s" alt="lcnc_swoop" /> <hr /> <a href="http://www.linuxcnc.org/docs/2.8/html/gui/qtdragon.html">QtDragon Documentation link</a> </body> </html> """ %(os.path.join(paths.IMAGEDIR,'lcnc_swoop.png')) STATUS.connect('general', self.dialog_return) STATUS.connect('state-on', lambda w: self.enable_onoff(True)) STATUS.connect('state-off', lambda w: self.enable_onoff(False)) STATUS.connect('mode-manual', lambda w: self.enable_auto(True)) STATUS.connect('mode-mdi', lambda w: self.enable_auto(True)) STATUS.connect('mode-auto', lambda w: self.enable_auto(False)) STATUS.connect('gcode-line-selected', lambda w, line: self.set_start_line(line)) STATUS.connect('hard-limits-tripped', self.hard_limit_tripped) STATUS.connect('interp-idle', lambda w: self.set_start_line(0)) STATUS.connect('program-pause-changed', lambda w, state: self.w.spindle_pause.setEnabled(state)) STATUS.connect('user-system-changed', self.user_system_changed) STATUS.connect('file-loaded', self.file_loaded) STATUS.connect('homed', self.homed) STATUS.connect('all-homed', self.all_homed) STATUS.connect('not-all-homed', self.not_all_homed) STATUS.connect('periodic', lambda w: self.update_runtimer()) STATUS.connect('command-running', lambda w: self.start_timer()) STATUS.connect('command-stopped', lambda w: self.stop_timer()) def class_patch__(self): self.old_fman = FM.load FM.load = self.load_code def initialized__(self): self.init_pins() self.init_preferences() self.init_widgets() self.w.stackedWidget_log.setCurrentIndex(0) self.w.stackedWidget.setCurrentIndex(0) self.w.stackedWidget_dro.setCurrentIndex(0) self.w.spindle_pause.setEnabled(False) self.w.btn_dimensions.setChecked(True) self.w.page_buttonGroup.buttonClicked.connect(self.main_tab_changed) self.w.filemanager.onUserClicked() self.w.filemanager_usb.onMediaClicked() self.w.filemanager.list.setAlternatingRowColors(False) self.w.filemanager_usb.list.setAlternatingRowColors(False) # hide widgets for A axis if not present if "A" not in INFO.AVAILABLE_AXES: for i in self.axis_a_list: self.w[i].hide() self.w.lbl_increments_linear.setText("INCREMENTS") # set validators for lineEdit widgets for val in self.lineedit_list: self.w['lineEdit_' + val].setValidator(self.valid) ############################# # SPECIAL FUNCTIONS SECTION # ############################# def init_pins(self): # spindle control pins pin = self.h.newpin("spindle_amps", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_pwr_changed) pin = self.h.newpin("spindle_volts", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_pwr_changed) pin = self.h.newpin("spindle_fault", hal.HAL_U32, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_fault_changed) pin = self.h.newpin("modbus-errors", hal.HAL_U32, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.mb_errors_changed) # external offset control pins self.h.newpin("eoffset_enable", hal.HAL_BIT, hal.HAL_OUT) self.h.newpin("eoffset_clear", hal.HAL_BIT, hal.HAL_OUT) self.h.newpin("eoffset_count", hal.HAL_S32, hal.HAL_OUT) pin = self.h.newpin("eoffset_value", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.eoffset_changed) def init_preferences(self): if not self.w.PREFS_: self.add_status("CRITICAL - no preference file found, enable preferences in screenoptions widget") return self.last_loaded_program = self.w.PREFS_.getpref('last_loaded_file', None, str,'BOOK_KEEPING') self.reload_tool = self.w.PREFS_.getpref('Tool to load', 0, int,'CUSTOM_FORM_ENTRIES') self.w.lineEdit_laser_x.setText(str(self.w.PREFS_.getpref('Laser X', 100, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_laser_y.setText(str(self.w.PREFS_.getpref('Laser Y', -20, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_x.setText(str(self.w.PREFS_.getpref('Sensor X', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_y.setText(str(self.w.PREFS_.getpref('Sensor Y', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_work_height.setText(str(self.w.PREFS_.getpref('Work Height', 20, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_touch_height.setText(str(self.w.PREFS_.getpref('Touch Height', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_height.setText(str(self.w.PREFS_.getpref('Sensor Height', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_search_vel.setText(str(self.w.PREFS_.getpref('Search Velocity', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_probe_vel.setText(str(self.w.PREFS_.getpref('Probe Velocity', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_max_probe.setText(str(self.w.PREFS_.getpref('Max Probe', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_eoffset_count.setText(str(self.w.PREFS_.getpref('Eoffset count', 0, int, 'CUSTOM_FORM_ENTRIES'))) self.w.chk_eoffsets.setChecked(self.w.PREFS_.getpref('External offsets', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_reload_program.setChecked(self.w.PREFS_.getpref('Reload program', False, bool,'CUSTOM_FORM_ENTRIES')) self.w.chk_reload_tool.setChecked(self.w.PREFS_.getpref('Reload tool', False, bool,'CUSTOM_FORM_ENTRIES')) self.w.chk_use_keyboard.setChecked(self.w.PREFS_.getpref('Use keyboard', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_run_from_line.setChecked(self.w.PREFS_.getpref('Run from line', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_use_virtual.setChecked(self.w.PREFS_.getpref('Use virtual keyboard', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_alpha_mode.setChecked(self.w.PREFS_.getpref('Use alpha display mode', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_inhibit_selection.setChecked(self.w.PREFS_.getpref('Inhibit display mouse selection', True, bool, 'CUSTOM_FORM_ENTRIES')) def closing_cleanup__(self): if not self.w.PREFS_: return self.w.PREFS_.putpref('last_loaded_directory', os.path.dirname(self.last_loaded_program), str, 'BOOK_KEEPING') self.w.PREFS_.putpref('last_loaded_file', self.last_loaded_program, str, 'BOOK_KEEPING') self.w.PREFS_.putpref('Tool to load', STATUS.get_current_tool(), int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Laser X', self.w.lineEdit_laser_x.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Laser Y', self.w.lineEdit_laser_y.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Sensor X', self.w.lineEdit_sensor_x.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Sensor Y', self.w.lineEdit_sensor_y.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Work Height', self.w.lineEdit_work_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Touch Height', self.w.lineEdit_touch_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Sensor Height', self.w.lineEdit_sensor_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Search Velocity', self.w.lineEdit_search_vel.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Probe Velocity', self.w.lineEdit_probe_vel.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Max Probe', self.w.lineEdit_max_probe.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Eoffset count', self.w.lineEdit_eoffset_count.text().encode('utf-8'), int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('External offsets', self.w.chk_eoffsets.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload program', self.w.chk_reload_program.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload tool', self.w.chk_reload_tool.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use keyboard', self.w.chk_use_keyboard.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Run from line', self.w.chk_run_from_line.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use virtual keyboard', self.w.chk_use_virtual.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use alpha display mode', self.w.chk_alpha_mode.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Inhibit display mouse selection', self.w.chk_inhibit_selection.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') def init_widgets(self): self.w.main_tab_widget.setCurrentIndex(0) self.w.slider_jog_linear.setMaximum(self.max_linear_velocity) self.w.slider_jog_linear.setValue(INFO.DEFAULT_LINEAR_JOG_VEL) self.w.slider_jog_angular.setMaximum(INFO.MAX_ANGULAR_JOG_VEL) self.w.slider_jog_angular.setValue(INFO.DEFAULT_ANGULAR_JOG_VEL) self.w.slider_maxv_ovr.setMaximum(self.max_linear_velocity) self.w.slider_maxv_ovr.setValue(self.max_linear_velocity) self.w.slider_feed_ovr.setMaximum(INFO.MAX_FEED_OVERRIDE) self.w.slider_feed_ovr.setValue(100) self.w.slider_rapid_ovr.setMaximum(100) self.w.slider_rapid_ovr.setValue(100) self.w.slider_spindle_ovr.setMinimum(INFO.MIN_SPINDLE_OVERRIDE) self.w.slider_spindle_ovr.setMaximum(INFO.MAX_SPINDLE_OVERRIDE) self.w.slider_spindle_ovr.setValue(100) self.w.chk_override_limits.setChecked(False) self.w.chk_override_limits.setEnabled(False) self.w.lbl_maxv_percent.setText("100 %") self.w.lbl_max_rapid.setText(str(self.max_linear_velocity)) self.w.lbl_home_x.setText(INFO.get_error_safe_setting('JOINT_0', 'HOME',"50")) self.w.lbl_home_y.setText(INFO.get_error_safe_setting('JOINT_1', 'HOME',"50")) self.w.cmb_gcode_history.addItem("No File Loaded") self.w.cmb_gcode_history.wheelEvent = lambda event: None self.w.jogincrements_linear.wheelEvent = lambda event: None self.w.jogincrements_angular.wheelEvent = lambda event: None self.w.gcode_editor.hide() #set up gcode list self.gcodes.setup_list() # clickable frames self.w.frame_cycle_start.mousePressEvent = self.btn_start_clicked self.w.frame_home_all.mousePressEvent = self.btn_home_all_clicked # web view widget for SETUP page self.web_view = QWebView() self.w.verticalLayout_setup.addWidget(self.web_view) self.web_view.setHtml(self.html) # check for virtual keyboard enabled if self.w.chk_use_virtual.isChecked(): self.w.btn_keyboard.show() else: self.w.btn_keyboard.hide() TOOLBAR.configure_statusbar(self.w.statusbar,'message_recall') def processed_focus_event__(self, receiver, event): if not self.w.chk_use_virtual.isChecked() or STATUS.is_auto_mode(): return if isinstance(receiver, QtWidgets.QLineEdit): if not receiver.isReadOnly(): self.w.stackedWidget_dro.setCurrentIndex(1) elif isinstance(receiver, QtWidgets.QTableView): self.w.stackedWidget_dro.setCurrentIndex(1) elif isinstance(receiver, QtWidgets.QCommonStyle): return def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2, QtCore.Qt.Key_F12): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, QtWidgets.QLineEdit): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break if isinstance(receiver2, TOOL_TABLE): flag = True break if isinstance(receiver2, OFFSET_VIEW): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True if event.isAutoRepeat():return True # ok if we got here then try keybindings try: KEYBIND.call(self,event,is_pressed,shift,cntrl) event.accept() return True except NameError as e: if is_pressed: LOG.debug('Exception in KEYBINDING: {}'.format (e)) self.add_status('Exception in KEYBINDING: {}'.format (e)) except Exception as e: if is_pressed: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s'%(KEYBIND.convert(event),key) event.accept() return True ######################### # CALLBACKS FROM STATUS # ######################### def spindle_pwr_changed(self, data): # this calculation assumes the voltage is line to neutral # and that the synchronous motor spindle has a power factor of 0.9 power = self.h['spindle_volts'] * self.h['spindle_amps'] * 2.7 # 3 x V x I x PF amps = "{:1.1f}".format(self.h['spindle_amps']) pwr = "{:1.1f}".format(power) self.w.lbl_spindle_amps.setText(amps) self.w.lbl_spindle_power.setText(pwr) def spindle_fault_changed(self, data): fault = hex(self.h['spindle_fault']) self.w.lbl_spindle_fault.setText(fault) def mb_errors_changed(self, data): errors = self.h['modbus-errors'] self.w.lbl_mb_errors.setText(str(errors)) def eoffset_changed(self, data): eoffset = "{:2.3f}".format(self.h['eoffset_value']) self.w.lbl_eoffset_value.setText(eoffset) def dialog_return(self, w, message): rtn = message.get('RETURN') name = message.get('NAME') plate_code = bool(message.get('ID') == '_touchplate_') sensor_code = bool(message.get('ID') == '_toolsensor_') wait_code = bool(message.get('ID') == '_wait_resume_') if plate_code and name == 'MESSAGE' and rtn is True: self.touchoff('touchplate') elif sensor_code and name == 'MESSAGE' and rtn is True: self.touchoff('sensor') elif wait_code and name == 'MESSAGE': self.h['eoffset_clear'] = False def user_system_changed(self, obj, data): sys = self.system_list[int(data) - 1] self.w.offset_table.selectRow(int(data) + 3) self.w.actionbutton_rel.setText(sys) def file_loaded(self, obj, filename): if filename is not None: self.add_status("Loaded file {}".format(filename)) self.w.progressBar.setValue(0) self.last_loaded_program = filename self.w.lbl_runtime.setText("00:00:00") else: self.add_status("Filename not valid") def percent_loaded_changed(self, fraction): if fraction <0: self.w.progressBar.setValue(0) self.w.progressBar.setFormat('Progress') else: self.w.progressBar.setValue(fraction) self.w.progressBar.setFormat('Loading: {}%'.format(fraction)) def percent_completed_changed(self, fraction): self.w.progressBar.setValue(fraction) if fraction <0: self.w.progressBar.setValue(0) self.w.progressBar.setFormat('Progress') else: self.w.progressBar.setFormat('Completed: {}%'.format(fraction)) def homed(self, obj, joint): i = int(joint) axis = INFO.GET_NAME_FROM_JOINT.get(i).lower() try: self.w["dro_axis_{}".format(axis)].setProperty('homed', True) self.w["dro_axis_{}".format(axis)].setStyle(self.w["dro_axis_{}".format(axis)].style()) except: pass def all_homed(self, obj): self.home_all = True self.w.lbl_home_all.setText("ALL\nHOMED") if self.first_turnon is True: self.first_turnon = False if self.w.chk_reload_tool.isChecked(): command = "M61 Q{}".format(self.reload_tool) ACTION.CALL_MDI(command) if self.last_loaded_program is not None and self.w.chk_reload_program.isChecked(): if os.path.isfile(self.last_loaded_program): self.w.cmb_gcode_history.addItem(self.last_loaded_program) self.w.cmb_gcode_history.setCurrentIndex(self.w.cmb_gcode_history.count() - 1) ACTION.OPEN_PROGRAM(self.last_loaded_program) self.w.manual_mode_button.setChecked(True) def not_all_homed(self, obj, list): self.home_all = False self.w.lbl_home_all.setText("HOME\nALL") for i in INFO.AVAILABLE_JOINTS: if str(i) in list: axis = INFO.GET_NAME_FROM_JOINT.get(i).lower() try: self.w["dro_axis_{}".format(axis)].setProperty('homed', False) self.w["dro_axis_{}".format(axis)].setStyle(self.w["dro_axis_{}".format(axis)].style()) except: pass def hard_limit_tripped(self, obj, tripped, list_of_tripped): self.add_status("Hard limits tripped") self.w.chk_override_limits.setEnabled(tripped) if not tripped: self.w.chk_override_limits.setChecked(False) def update_runtimer(self): if self.timerOn is False or STATUS.is_auto_paused(): return self.time_tenths += 1 if self.time_tenths == 10: self.time_tenths = 0 self.run_time += 1 hours, remainder = divmod(self.run_time, 3600) minutes, seconds = divmod(remainder, 60) self.w.lbl_runtime.setText("{:02d}:{:02d}:{:02d}".format(hours, minutes, seconds)) def start_timer(self): if STATUS.is_auto_running(): self.run_time = 0 self.timerOn = True def stop_timer(self): self.timerOn = False ####################### # CALLBACKS FROM FORM # ####################### # main button bar def main_tab_changed(self, btn): if STATUS.is_auto_mode(): self.add_status("Cannot switch pages while in AUTO mode") self.w.btn_main.setChecked(True) return index = btn.property("index") if index is None: return self.w.main_tab_widget.setCurrentIndex(index) if index == TAB_MAIN: self.w.stackedWidget.setCurrentIndex(0) elif index == TAB_FILE: self.w.stackedWidget.setCurrentIndex(1) elif index == TAB_OFFSETS: self.w.stackedWidget.setCurrentIndex(2) elif index == TAB_TOOL: self.w.stackedWidget.setCurrentIndex(3) else: self.w.stackedWidget.setCurrentIndex(0) # gcode frame def cmb_gcode_history_clicked(self): if self.w.cmb_gcode_history.currentIndex() == 0: return filename = self.w.cmb_gcode_history.currentText().encode('utf-8') if filename == self.last_loaded_program: self.add_status("Selected program is already loaded") else: ACTION.OPEN_PROGRAM(filename) # program frame def btn_start_clicked(self, obj): if self.w.main_tab_widget.currentIndex() != 0: return if not STATUS.is_auto_mode(): self.add_status("Must be in AUTO mode to run a program") return start_line = int(self.w.lbl_start_line.text().encode('utf-8')) self.add_status("Started program from line {}".format(start_line)) self.run_time = 0 if start_line == 1: ACTION.RUN(start_line) else: # instantiate preset dialog info = '<b>Running from Line: {} <\b>'.format(start_line) mess = {'NAME':'RUNFROMLINE', 'TITLE':'Preset Dialog', 'ID':'_RUNFROMLINE', 'MESSAGE':info, 'LINE':start_line} ACTION.CALL_DIALOG(mess) def btn_reload_file_clicked(self): if self.last_loaded_program: self.w.progressBar.setValue(0) self.add_status("Loaded program file {}".format(self.last_loaded_program)) ACTION.OPEN_PROGRAM(self.last_loaded_program) # DRO frame def btn_home_all_clicked(self, obj): if self.home_all is False: ACTION.SET_MACHINE_HOMING(-1) else: ACTION.SET_MACHINE_UNHOMED(-1) def btn_home_clicked(self): joint = self.w.sender().property('joint') axis = INFO.GET_NAME_FROM_JOINT.get(joint).lower() if self.w["dro_axis_{}".format(axis)].property('homed') is True: ACTION.SET_MACHINE_UNHOMED(joint) else: ACTION.SET_MACHINE_HOMING(joint) # tool frame def disable_pause_buttons(self, state): self.w.action_pause.setEnabled(not state) self.w.action_step.setEnabled(not state) if state: # set external offsets to lift spindle self.h['eoffset_enable'] = self.w.chk_eoffsets.isChecked() fval = float(self.w.lineEdit_eoffset_count.text()) self.h['eoffset_count'] = int(fval) else: self.h['eoffset_count'] = 0 self.h['eoffset_clear'] = True # instantiate warning box info = "Wait for spindle at speed signal before resuming" mess = {'NAME':'MESSAGE', 'ICON':'WARNING', 'ID':'_wait_resume_', 'MESSAGE':'CAUTION', 'MORE':info, 'TYPE':'OK'} ACTION.CALL_DIALOG(mess) # override frame def slow_button_clicked(self, state): slider = self.w.sender().property('slider') current = self.w[slider].value() max = self.w[slider].maximum() if state: self.w.sender().setText("SLOW") self.w[slider].setMaximum(max / self.slow_jog_factor) self.w[slider].setValue(current / self.slow_jog_factor) self.w[slider].setPageStep(10) else: self.w.sender().setText("FAST") self.w[slider].setMaximum(max * self.slow_jog_factor) self.w[slider].setValue(current * self.slow_jog_factor) self.w[slider].setPageStep(100) def slider_maxv_changed(self, value): maxpc = (float(value) / self.max_linear_velocity) * 100 self.w.lbl_maxv_percent.setText("{:3.0f} %".format(maxpc)) def slider_rapid_changed(self, value): rapid = (float(value) / 100) * self.max_linear_velocity self.w.lbl_max_rapid.setText("{:4.0f}".format(rapid)) def btn_maxv_100_clicked(self): self.w.slider_maxv_ovr.setValue(self.max_linear_velocity) def btn_maxv_50_clicked(self): self.w.slider_maxv_ovr.setValue(self.max_linear_velocity / 2) # file tab def btn_gcode_edit_clicked(self, state): if not STATUS.is_on_and_idle(): return if state: self.w.filemanager.hide() self.w.widget_file_copy.hide() self.w.gcode_editor.show() self.w.gcode_editor.editMode() else: self.w.filemanager.show() self.w.widget_file_copy.show() self.w.gcode_editor.hide() self.w.gcode_editor.readOnlyMode() def btn_load_file_clicked(self): fname = self.w.filemanager.getCurrentSelected() if fname[1] is True: self.load_code(fname[0]) def btn_copy_file_clicked(self): if self.w.sender() == self.w.btn_copy_right: source = self.w.filemanager_usb.getCurrentSelected() target = self.w.filemanager.getCurrentSelected() elif self.w.sender() == self.w.btn_copy_left: source = self.w.filemanager.getCurrentSelected() target = self.w.filemanager_usb.getCurrentSelected() else: return if source[1] is False: self.add_status("Specified source is not a file") return if target[1] is True: destination = os.path.join(os.path.dirname(target[0]), os.path.basename(source[0])) else: destination = os.path.join(target[0], os.path.basename(source[0])) try: copyfile(source[0], destination) self.add_status("Copied file from {} to {}".format(source[0], destination)) except Exception as e: self.add_status("Unable to copy file. %s" %e) # offsets tab def btn_goto_sensor_clicked(self): x = float(self.w.lineEdit_sensor_x.text()) y = float(self.w.lineEdit_sensor_y.text()) if not STATUS.is_metric_mode(): x = x / 25.4 y = y / 25.4 ACTION.CALL_MDI("G90") ACTION.CALL_MDI_WAIT("G53 G0 Z0") command = "G53 G0 X{:3.4f} Y{:3.4f}".format(x, y) ACTION.CALL_MDI_WAIT(command, 10) def btn_ref_laser_clicked(self): x = float(self.w.lineEdit_laser_x.text()) y = float(self.w.lineEdit_laser_y.text()) if not STATUS.is_metric_mode(): x = x / 25.4 y = y / 25.4 self.add_status("Laser offsets set") command = "G10 L20 P0 X{:3.4f} Y{:3.4f}".format(x, y) ACTION.CALL_MDI(command) # tool tab def btn_m61_clicked(self): checked = self.w.tooloffsetview.get_checked_list() if len(checked) > 1: self.add_status("Select only 1 tool to load") elif checked: self.add_status("Loaded tool {}".format(checked[0])) ACTION.CALL_MDI("M61 Q{}".format(checked[0])) else: self.add_status("No tool selected") def btn_touchoff_clicked(self): if STATUS.get_current_tool() == 0: self.add_status("Cannot touchoff with no tool loaded") return if not STATUS.is_all_homed(): self.add_status("Must be homed to perform tool touchoff") return # instantiate dialog box sensor = self.w.sender().property('sensor') info = "Ensure tooltip is within {} mm of tool sensor and click OK".format(self.w.lineEdit_max_probe.text()) mess = {'NAME':'MESSAGE', 'ID':sensor, 'MESSAGE':'TOOL TOUCHOFF', 'MORE':info, 'TYPE':'OKCANCEL'} ACTION.CALL_DIALOG(mess) # status tab def btn_clear_status_clicked(self): STATUS.emit('update-machine-log', None, 'DELETE') def btn_save_status_clicked(self): text = self.w.machinelog.toPlainText() filename = self.w.lbl_clock.text().encode('utf-8') filename = 'status_' + filename.replace(' ','_') + '.txt' self.add_status("Saving Status file to {}".format(filename)) with open(filename, 'w') as f: f.write(text) def btn_dimensions_clicked(self, state): self.w.gcodegraphics.show_extents_option = state self.w.gcodegraphics.clear_live_plotter() def chk_override_limits_checked(self, state): if state: print("Override limits set") ACTION.SET_LIMITS_OVERRIDE() else: print("Override limits not set") def chk_run_from_line_checked(self, state): if not state: self.w.lbl_start_line.setText('1') def chk_alpha_mode_clicked(self, state): self.w.gcodegraphics.set_alpha_mode(state) def chk_inhibit_display_selection_clicked(self, state): self.w.gcodegraphics.set_inhibit_selection(state) # settings tab def chk_use_virtual_changed(self, state): if not state: self.w.stackedWidget_dro.setCurrentIndex(0) ##################### # GENERAL FUNCTIONS # ##################### def load_code(self, fname): if fname is None: return if fname.endswith(".ngc") or fname.endswith(".py"): self.w.cmb_gcode_history.addItem(fname) self.w.cmb_gcode_history.setCurrentIndex(self.w.cmb_gcode_history.count() - 1) ACTION.OPEN_PROGRAM(fname) self.add_status("Loaded program file : {}".format(fname)) self.w.main_tab_widget.setCurrentIndex(TAB_MAIN) elif fname.endswith(".html"): self.web_view.load(QtCore.QUrl.fromLocalFile(fname)) self.add_status("Loaded HTML file : {}".format(fname)) self.w.main_tab_widget.setCurrentIndex(TAB_SETUP) self.w.btn_setup.setChecked(True) else: self.add_status("Unknown or invalid filename") def disable_spindle_pause(self): self.h['eoffset_count'] = 0 if self.w.spindle_pause.isChecked(): self.w.spindle_pause.setChecked(False) def touchoff(self, selector): if selector == 'touchplate': z_offset = float(self.w.lineEdit_touch_height.text()) elif selector == 'sensor': z_offset = float(self.w.lineEdit_sensor_height.text()) - float(self.w.lineEdit_work_height.text()) else: self.add_alarm("Unknown touchoff routine specified") return self.add_status("Touchoff to {} started".format(selector)) max_probe = self.w.lineEdit_max_probe.text() search_vel = self.w.lineEdit_search_vel.text() probe_vel = self.w.lineEdit_probe_vel.text() ACTION.CALL_MDI("G21 G49") ACTION.CALL_MDI("G10 L20 P0 Z0") ACTION.CALL_MDI("G91") command = "G38.2 Z-{} F{}".format(max_probe, search_vel) if ACTION.CALL_MDI_WAIT(command, 10) == -1: ACTION.CALL_MDI("G90") return if ACTION.CALL_MDI_WAIT("G1 Z4.0"): ACTION.CALL_MDI("G90") return ACTION.CALL_MDI("G4 P0.5") command = "G38.2 Z-4.4 F{}".format(probe_vel) if ACTION.CALL_MDI_WAIT(command, 10) == -1: ACTION.CALL_MDI("G90") return command = "G10 L20 P0 Z{}".format(z_offset) ACTION.CALL_MDI_WAIT(command) command = "G1 Z10 F{}".format(search_vel) ACTION.CALL_MDI_WAIT(command) ACTION.CALL_MDI("G90") def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): self.add_status('Machine must be ON and in Manual mode to jog') return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte()/60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) def add_status(self, message): self._m = message print message self.w.statusbar.showMessage(self._m, 5000) STATUS.emit('update-machine-log', self._m, 'TIME') def enable_auto(self, state): for widget in self.auto_list: self.w[widget].setEnabled(state) if state is True: self.w.jogging_frame.show() else: self.w.jogging_frame.hide() self.w.main_tab_widget.setCurrentIndex(TAB_MAIN) self.w.stackedWidget.setCurrentIndex(0) self.w.stackedWidget_dro.setCurrentIndex(0) def enable_onoff(self, state): if state: self.add_status("Machine ON") else: self.add_status("Machine OFF") self.w.spindle_pause.setChecked(False) self.h['eoffset_count'] = 0 for widget in self.onoff_list: self.w[widget].setEnabled(state) def set_start_line(self, line): if line == 0: self.w.lbl_start_line.setText('1') elif self.w.chk_run_from_line.isChecked(): self.w.lbl_start_line.setText(str(line)) else: self.w.lbl_start_line.setText('1') def use_keyboard(self): if self.w.chk_use_keyboard.isChecked(): return True else: self.add_status('Keyboard shortcuts are disabled') return False def add_alarm(self, message): STATUS.emit('update-machine-log', message, 'TIME') ##################### # KEY BINDING CALLS # ##################### def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(True) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(False) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: ACTION.ABORT() def on_keycall_HOME(self,event,state,shift,cntrl): if state and not STATUS.is_all_homed() and self.use_keyboard(): ACTION.SET_MACHINE_HOMING(-1) def on_keycall_pause(self,event,state,shift,cntrl): if state and STATUS.is_auto_mode() and self.use_keyboard(): ACTION.PAUSE() def on_keycall_XPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 3, -1, shift, False) def on_keycall_F12(self,event,state,shift,cntrl): if state: STYLEEDITOR.load_dialog() ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)