class HtmlRender(object): def __init__(self, url, baseurl=None): self.url = url self.web_view = QWebView() self.network_manager = SplashQNetworkAccessManager() self.web_page = SplashQWebPage() self.web_page.setNetworkAccessManager(self.network_manager) self.web_view.setPage(self.web_page) self.web_view.setAttribute(Qt.WA_DeleteOnClose, True) settings = self.web_view.settings() settings.setAttribute(QWebSettings.JavascriptEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, False) settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) self.web_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.deferred = defer.Deferred() request = QNetworkRequest() request.setUrl(QUrl(url)) if baseurl: self._baseUrl = QUrl(baseurl) self.network_manager.finished.connect(self._requestFinished) self.network_manager.get(request) else: self.web_page.loadFinished.connect(self._loadFinished) self.web_page.mainFrame().load(request) def close(self): self.web_view.stop() self.web_view.close() self.web_page.deleteLater() self.web_view.deleteLater() self.network_manager.deleteLater() def _requestFinished(self, reply): self.web_page.networkAccessManager().finished.disconnect(self._requestFinished) self.web_view.loadFinished.connect(self._loadFinished) mimeType = reply.header(QNetworkRequest.ContentTypeHeader).toString() self.web_view.page().mainFrame().setContent(reply.readAll(), mimeType, self._baseUrl) def _loadFinished(self, ok): if self.deferred.called: return if ok: try: self.deferred.callback(self._render()) except: self.deferred.errback() else: self.deferred.errback(RenderError()) def _render(self): frame = self.web_view.page().mainFrame() return str(frame.toHtml().toUtf8())
def run(self): print "Displaying" app=QApplication(sys.argv) window=QWebView() window.setHtml(self.__html) window.show() app.exec_() app.exit() window.close() del app del window print "Closing Display."
class OAuth2Application(QApplication): def __init__(self, oa_url, oa_result_base, args): super(OAuth2Application, self).__init__(args) self.oa_result_base = oa_result_base self.oa_result = oa_result_base self.browser = QWebView() self.browser.loadFinished.connect(self.__result_available) self.browser.load(QUrl(oa_url)) self.browser.show() self.exec_() def __result_available(self, ok): current_url = self.browser.url().toString() if self.oa_result_base in current_url: self.oa_result = current_url self.browser.close() self.exit() def check_success(self): return self.oa_result != self.oa_result_base
class WebpageRender(object): def __init__(self, network_manager, splash_proxy_factory, splash_request, verbose=False): self.network_manager = network_manager self.web_view = QWebView() self.web_page = SplashQWebPage() self.web_page.setNetworkAccessManager(self.network_manager) self.web_view.setPage(self.web_page) self.web_view.setAttribute(Qt.WA_DeleteOnClose, True) settings = self.web_page.settings() settings.setAttribute(QWebSettings.JavascriptEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, False) settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) settings.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, True) self.web_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.splash_request = splash_request self.web_page.splash_request = splash_request self.web_page.splash_proxy_factory = splash_proxy_factory self.verbose = verbose self.deferred = defer.Deferred() # ======= General request/response handling: def doRequest( self, url, baseurl=None, wait_time=None, viewport=None, js_source=None, js_profile=None, console=False ): self.url = url self.wait_time = defaults.WAIT_TIME if wait_time is None else wait_time self.js_source = js_source self.js_profile = js_profile self.console = console self.viewport = defaults.VIEWPORT if viewport is None else viewport request = QNetworkRequest() request.setUrl(QUrl(url.decode("utf8"))) if self.viewport != "full": # viewport='full' can't be set if content is not loaded yet self._setViewportSize(self.viewport) if getattr(self.splash_request, "pass_headers", False): headers = self.splash_request.getAllHeaders() for name, value in headers.items(): request.setRawHeader(name, value) if name.lower() == "user-agent": self.web_page.custom_user_agent = value if baseurl: self._baseUrl = QUrl(baseurl.decode("utf8")) request.setOriginatingObject(self.web_page.mainFrame()) self._reply = self.network_manager.get(request) self._reply.finished.connect(self._requestFinished) else: self.web_page.loadFinished.connect(self._loadFinished) if self.splash_request.method == "POST": self.web_page.mainFrame().load( request, QNetworkAccessManager.PostOperation, self.splash_request.content.getvalue() ) else: self.web_page.mainFrame().load(request) def close(self): self.web_view.stop() self.web_view.close() self.web_page.deleteLater() self.web_view.deleteLater() def _requestFinished(self): self.log("_requestFinished %s" % id(self.splash_request)) self.web_page.loadFinished.connect(self._loadFinished) mimeType = self._reply.header(QNetworkRequest.ContentTypeHeader).toString() data = self._reply.readAll() self.web_page.mainFrame().setContent(data, mimeType, self._baseUrl) if self._reply.error(): log.msg("Error loading %s: %s" % (self.url, self._reply.errorString()), system="render") self._reply.close() self._reply.deleteLater() def _loadFinished(self, ok): self.log("_loadFinished %s" % id(self.splash_request)) if self.deferred.called: # sometimes this callback is called multiple times self.log("_loadFinished called multiple times") return page_ok = ok and self.web_page.errorInfo is None maybe_redirect = not ok and self.web_page.errorInfo is None error_loading = ok and self.web_page.errorInfo is not None if maybe_redirect: self.log("Redirect or other non-fatal error detected %s" % id(self.splash_request)) # XXX: It assumes loadFinished will be called again because # redirect happens. If redirect is detected improperly, # loadFinished won't be called again, and Splash will return # the result only after a timeout. # # FIXME: This can happen if server returned incorrect # Content-Type header; there is no an additional loadFinished # signal in this case. return if page_ok: time_ms = int(self.wait_time * 1000) QTimer.singleShot(time_ms, self._loadFinishedOK) elif error_loading: self.log("loadFinished %s: %s" % (id(self.splash_request), str(self.web_page.errorInfo))) # , min_level=1) # XXX: maybe return a meaningful error page instead of generic # error message? self.deferred.errback(RenderError()) else: self.log("loadFinished %s: unknown error" % id(self.splash_request)) # , min_level=1) self.deferred.errback(RenderError()) def _loadFinishedOK(self): self.log("_loadFinishedOK %s" % id(self.splash_request)) try: self._prerender() self.deferred.callback(self._render()) except: self.deferred.errback() # ======= Rendering methods that subclasses can use: def _getHtml(self): frame = self.web_page.mainFrame() return bytes(frame.toHtml().toUtf8()) def _getPng(self, width=None, height=None): image = QImage(self.web_page.viewportSize(), QImage.Format_ARGB32) painter = QPainter(image) self.web_page.mainFrame().render(painter) painter.end() if width: image = image.scaledToWidth(width, Qt.SmoothTransformation) if height: image = image.copy(0, 0, width, height) b = QBuffer() image.save(b, "png") return bytes(b.data()) def _getIframes(self, children=True, html=True): frame = self.web_page.mainFrame() return self._frameToDict(frame, children, html) def _render(self): raise NotImplementedError() # ======= Other helper methods: def _setViewportSize(self, viewport): w, h = map(int, viewport.split("x")) size = QSize(w, h) self.web_page.setViewportSize(size) def _setFullViewport(self): size = self.web_page.mainFrame().contentsSize() if size.isEmpty(): self.log("contentsSize method doesn't work %s" % id(self.splash_request)) self._setViewportSize(defaults.VIEWPORT_FALLBACK) else: self.web_page.setViewportSize(size) def _loadJsLibs(self, frame, js_profile): if js_profile: for jsfile in os.listdir(js_profile): if jsfile.endswith(".js"): with open(os.path.join(js_profile, jsfile)) as f: frame.evaluateJavaScript(f.read().decode("utf-8")) def _runJS(self, js_source, js_profile): js_output = None js_console_output = None if js_source: frame = self.web_page.mainFrame() if self.console: js_console = JavascriptConsole() frame.addToJavaScriptWindowObject("console", js_console) if js_profile: self._loadJsLibs(frame, js_profile) ret = frame.evaluateJavaScript(js_source) js_output = bytes(ret.toString().toUtf8()) if self.console: js_console_output = [bytes(s.toUtf8()) for s in js_console.messages] return js_output, js_console_output def _frameToDict(self, frame, children=True, html=True): g = frame.geometry() res = { "url": unicode(frame.url().toString()), "requestedUrl": unicode(frame.requestedUrl().toString()), "geometry": (g.x(), g.y(), g.width(), g.height()), "title": unicode(frame.title()), } if html: res["html"] = unicode(frame.toHtml()) if children: res["childFrames"] = [self._frameToDict(f, True, html) for f in frame.childFrames()] res["frameName"] = unicode(frame.frameName()) return res def _prerender(self): if self.viewport == "full": self._setFullViewport() self.js_output, self.js_console_output = self._runJS(self.js_source, self.js_profile) def log(self, text): if self.verbose: log.msg(text, system="render")
class AwBrowser(QDialog): """ Customization and configuration of a web browser to run within Anki """ _parent = None _fields = [] _selectedListener = None _web = None _urlInfo = None def __init__(self, myParent): QDialog.__init__(self, myParent) self._parent = myParent self.setupUI() def setupUI(self): self.setWindowTitle('Anki :: Web Browser Addon') self.setGeometry(450, 200, 800, 450) self.setMinimumWidth (640) self.setMinimumHeight(450) mainLayout = QVBoxLayout() mainLayout.setContentsMargins(0,0,0,0) mainLayout.setSpacing(0) self.setLayout(mainLayout) topWidget = QtWidgets.QWidget(self) topWidget.setFixedHeight(50) topLayout = QtWidgets.QHBoxLayout(topWidget) topLayout.setObjectName("topLayout") lbSite = QtWidgets.QLabel(topWidget) lbSite.setObjectName("label") lbSite.setText("Website: ") topLayout.addWidget(lbSite) self._itAddress = QtWidgets.QLineEdit(topWidget) self._itAddress.setObjectName("itSite") topLayout.addWidget(self._itAddress) cbGo = QtWidgets.QCommandLinkButton(topWidget) cbGo.setObjectName("cbGo") cbGo.setFixedSize(30, 30) topLayout.addWidget(cbGo) # cbImport = QtWidgets.QCommandLinkButton(topWidget) # cbImport.setObjectName("cbImport") # cbImport.setFixedSize(30, 30) # topLayout.addWidget(cbImport) self._loadingBar = QtWidgets.QProgressBar(topWidget) self._loadingBar.setFixedWidth(100) self._loadingBar.setProperty("value", 100) self._loadingBar.setObjectName("loadingBar") topLayout.addWidget(self._loadingBar) mainLayout.addWidget(topWidget) self._web = QWebView(self) self._web.contextMenuEvent = self.contextMenuEvent self._web.page().loadStarted.connect(self.onStartLoading) self._web.page().loadFinished.connect(self.onLoadFinish) self._web.page().loadProgress.connect(self.onProgress) self._web.urlChanged.connect(self.onPageChange) cbGo.clicked.connect(self._goToAddress) mainLayout.addWidget(self._web) if cfg.getConfig().browserAlwaysOnTop: self.setWindowFlags(Qt.WindowStaysOnTopHint) def formatTargetURL(self, website: str, query: str = ''): return website.format(urllib.parse.quote(query, encoding='utf8')) #encode('utf8', 'ignore') def open(self, website, query: str): """ Loads a given page with its replacing part with its query, and shows itself """ target = self.formatTargetURL(website, query) self._web.load(QUrl( target )) self._itAddress.setText(target) self.show() return self._web def unload(self): try: self._web.setHtml(BLANK_PAGE) except Exception: pass def onClose(self): self._parent = None self._web.close() self.close() def onStartLoading(self): self._loadingBar.setProperty("value", 1) def onProgress(self, prog): self._loadingBar.setProperty("value", prog) def onLoadFinish(self, result): self._loadingBar.setProperty("value", 100) if not result: Feedback.showInfo('Error loading page') Feedback.log('Error on loading page! ', result) def _goToAddress(self): self._web.load(QUrl( self._itAddress.text() )) self._web.show() def onPageChange(self, url): self._itAddress.setText(url.toString()) def welcome(self): self._web.setHtml(WELCOME_PAGE) self.show() # ------------------------------------ Menu --------------------------------------- def _makeMenuAction(self, field, value, isLink): """ Creates correct operations for the context menu selection. Only with lambda, it would repeat only the last element """ return lambda: self._selectedListener.handleSelection(field, value, isLink) def contextMenuEvent(self, evt): """ Handles the context menu in the web view. Shows and handle options (from field list), only if in edit mode. """ if not (self._fields and self._selectedListener): return isLink = False value = None if self._web.selectedText(): isLink = False value = self._web.selectedText() else: if (self._web.page().contextMenuData().mediaType() == QWebHitTestResult.MediaTypeImage and self._web.page().contextMenuData().mediaUrl()): isLink = True value = self._web.page().contextMenuData().mediaUrl() if not value: return self.createCtxMenu(value, isLink, evt) def createCtxMenu(self, value, isLink, evt): 'Creates and configures the menu itself' m = QMenu(self) sub = QMenu(Label.BROWSER_ASSIGN_TO, m) m.setTitle(Label.BROWSER_ASSIGN_TO) for index, label in self._fields.items(): act = QAction(label, m, triggered=self._makeMenuAction(index, value, isLink)) sub.addAction(act) m.addMenu(sub) action = m.exec_(self.mapToGlobal(evt.pos())) def load(self, qUrl): self._web.load(qUrl) # ----------------- getter / setter ------------------- def setFields(self, fList): self._fields = fList def setSelectionListener(self, value): self._selectedListener = value
class BrowserTab(QObject): """ An object for controlling a single browser tab (QWebView). It is created by splash.pool.Pool. Pool attaches to tab's deferred and waits until either a callback or an errback is called, then destroys a BrowserTab. XXX: currently cookies are not shared between "browser tabs". """ def __init__(self, network_manager, splash_proxy_factory, verbosity, render_options): """ Create a new browser tab. """ QObject.__init__(self) self.deferred = defer.Deferred() self.network_manager = network_manager self.verbosity = verbosity self._uid = render_options.get_uid() self._closing = False self._active_timers = set() self._timers_to_cancel_on_redirect = weakref.WeakKeyDictionary() # timer: callback self._timers_to_cancel_on_error = weakref.WeakKeyDictionary() # timer: callback self._js_console = None self._history = [] self._autoload_scripts = [] self._init_webpage(verbosity, network_manager, splash_proxy_factory, render_options) self._setup_logging(verbosity) self.http_client = _SplashHttpClient(self.web_page) def _init_webpage(self, verbosity, network_manager, splash_proxy_factory, render_options): """ Create and initialize QWebPage and QWebView """ self.web_page = SplashQWebPage(verbosity) self.web_page.setNetworkAccessManager(network_manager) self.web_page.splash_proxy_factory = splash_proxy_factory self.web_page.render_options = render_options self._set_default_webpage_options(self.web_page) self._setup_webpage_events() self.web_view = QWebView() self.web_view.setPage(self.web_page) self.web_view.setAttribute(Qt.WA_DeleteOnClose, True) def _set_default_webpage_options(self, web_page): """ Set QWebPage options. TODO: allow to customize them. """ settings = web_page.settings() settings.setAttribute(QWebSettings.JavascriptEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, False) settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) settings.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, True) web_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) def _setup_logging(self, verbosity): """ Setup logging of various events """ self.logger = _BrowserTabLogger( uid=self._uid, web_page=self.web_page, verbosity=verbosity, ) self.logger.enable() def _setup_webpage_events(self): self._load_finished = WrappedSignal(self.web_page.mainFrame().loadFinished) self.web_page.mainFrame().loadFinished.connect(self._on_load_finished) self.web_page.mainFrame().urlChanged.connect(self._on_url_changed) self.web_page.mainFrame().javaScriptWindowObjectCleared.connect(self._on_javascript_window_object_cleared) def return_result(self, result): """ Return a result to the Pool. """ if self._result_already_returned(): self.logger.log("error: result is already returned", min_level=1) self.deferred.callback(result) # self.deferred = None def return_error(self, error=None): """ Return an error to the Pool. """ if self._result_already_returned(): self.logger.log("error: result is already returned", min_level=1) self.deferred.errback(error) # self.deferred = None def _result_already_returned(self): """ Return True if an error or a result is already returned to Pool """ return self.deferred.called def set_custom_headers(self, headers): """ Set custom HTTP headers to be sent with each request. Passed headers are merged with QWebKit default headers, overwriting QWebKit values in case of conflicts. """ self.web_page.custom_headers = headers def set_images_enabled(self, enabled): self.web_page.settings().setAttribute(QWebSettings.AutoLoadImages, enabled) def set_viewport(self, size): """ Set viewport size. If size is "full" viewport size is detected automatically. If can also be "<width>x<height>". """ if size == 'full': size = self.web_page.mainFrame().contentsSize() if size.isEmpty(): self.logger.log("contentsSize method doesn't work %s", min_level=1) size = defaults.VIEWPORT_FALLBACK if not isinstance(size, QSize): w, h = map(int, size.split('x')) size = QSize(w, h) self.web_page.setViewportSize(size) w, h = int(size.width()), int(size.height()) self.logger.log("viewport size is set to %sx%s" % (w, h), min_level=2) return w, h def lock_navigation(self): self.web_page.navigation_locked = True def unlock_navigation(self): self.web_page.navigation_locked = False def set_content(self, data, callback, errback, mime_type=None, baseurl=None): """ Set page contents to ``data``, then wait until page loads. Invoke a callback if load was successful or errback if it wasn't. """ if mime_type is None: mime_type = "text/html; charset=utf-8" if baseurl is None: baseurl = '' if isinstance(data, unicode): data = data.encode('utf8') callback_id = self._load_finished.connect( self._on_content_ready, callback=callback, errback=errback, ) self.logger.log("callback %s is connected to loadFinished" % callback_id, min_level=3) self.web_page.mainFrame().setContent(data, mime_type, QUrl(baseurl)) def set_user_agent(self, value): """ Set User-Agent header for future requests """ self.http_client.set_user_agent(value) def get_cookies(self): """ Return a list of all cookies in the current cookiejar """ return cookies2har(self.web_page.cookiejar.allCookies()) def init_cookies(self, cookies): """ Replace all current cookies with ``cookies`` """ self.web_page.cookiejar.init(cookies) def clear_cookies(self): """ Remove all cookies. Return a number of cookies deleted. """ return self.web_page.cookiejar.clear() def delete_cookies(self, name=None, url=None): """ Delete cookies with name == ``name``. If ``url`` is not None then only those cookies are deleted wihch are to be added when a request is sent to ``url``. Return a number of cookies deleted. """ return self.web_page.cookiejar.delete(name, url) def add_cookie(self, cookie): return self.web_page.cookiejar.add(cookie) @property def url(self): """ Current URL """ return unicode(self.web_page.mainFrame().url().toString()) def go(self, url, callback, errback, baseurl=None, http_method='GET', body=None, headers=None): """ Go to an URL. This is similar to entering an URL in address tab and pressing Enter. """ self.store_har_timing("_onStarted") if baseurl: # If baseurl is used, we download the page manually, # then set its contents to the QWebPage and let it # download related resources and render the result. cb = functools.partial( self._on_baseurl_request_finished, callback=callback, errback=errback, baseurl=baseurl, url=url, ) self.http_client.request(url, callback=cb, method=http_method, body=body, headers=headers, follow_redirects=True, ) else: # if not self._goto_callbacks.isempty(): # self.logger.log("Only a single concurrent 'go' request is supported. " # "Previous go requests will be cancelled.", min_level=1) # # When a new URL is loaded to mainFrame an errback will # # be called, so we're not cancelling this callback manually. callback_id = self._load_finished.connect( self._on_content_ready, callback=callback, errback=errback, ) self.logger.log("callback %s is connected to loadFinished" % callback_id, min_level=3) self._load_url_to_mainframe(url, http_method, body, headers=headers) def stop_loading(self): """ Stop loading of the current page and all pending page refresh/redirect requests. """ self.logger.log("stop_loading", min_level=2) self.web_view.pageAction(QWebPage.StopScheduledPageRefresh) self.web_view.stop() def close(self): """ Destroy this tab """ self._closing = True self.web_view.pageAction(QWebPage.StopScheduledPageRefresh) self.web_view.stop() self.web_view.close() self.web_page.deleteLater() self.web_view.deleteLater() self._cancel_all_timers() @skip_if_closing def _on_load_finished(self, ok): if self.web_page.maybe_redirect(ok): self.logger.log("Redirect or other non-fatal error detected", min_level=2) return if self.web_page.is_ok(ok): # or maybe_redirect: self.logger.log("loadFinished: ok", min_level=2) else: self._cancel_timers(self._timers_to_cancel_on_error) if self.web_page.error_loading(ok): self.logger.log("loadFinished: %s" % (str(self.web_page.error_info)), min_level=1) else: self.logger.log("loadFinished: unknown error", min_level=1) def _on_baseurl_request_finished(self, callback, errback, baseurl, url): """ This method is called when ``baseurl`` is used and a reply for the first request is received. """ self.logger.log("baseurl_request_finished", min_level=2) reply = self.sender() mime_type = reply.header(QNetworkRequest.ContentTypeHeader).toString() data = reply.readAll() self.set_content( data=data, callback=callback, errback=errback, mime_type=mime_type, baseurl=baseurl, ) if reply.error(): self.logger.log("Error loading %s: %s" % (url, reply.errorString()), min_level=1) def _load_url_to_mainframe(self, url, http_method, body=None, headers=None): request = self.http_client.request_obj(url, headers=headers) meth = OPERATION_QT_CONSTANTS[http_method] if body is None: # PyQT doesn't support body=None self.web_page.mainFrame().load(request, meth) else: self.web_page.mainFrame().load(request, meth, body) @skip_if_closing def _on_content_ready(self, ok, callback, errback, callback_id): """ This method is called when a QWebPage finishes loading its contents. """ if self.web_page.maybe_redirect(ok): # XXX: It assumes loadFinished will be called again because # redirect happens. If redirect is detected improperly, # loadFinished won't be called again, and Splash will return # the result only after a timeout. return self.logger.log("loadFinished: disconnecting callback %s" % callback_id, min_level=3) self._load_finished.disconnect(callback_id) if self.web_page.is_ok(ok): callback() elif self.web_page.error_loading(ok): # XXX: maybe return a meaningful error page instead of generic # error message? errback() # errback(RenderError()) else: errback() # errback(RenderError()) def wait(self, time_ms, callback, onredirect=None, onerror=None): """ Wait for time_ms, then run callback. If onredirect is True then the timer is cancelled if redirect happens. If onredirect is callable then in case of redirect the timer is cancelled and this callable is called. If onerror is True then the timer is cancelled if a render error happens. If onerror is callable then in case of a render error the timer is cancelled and this callable is called. """ timer = QTimer() timer.setSingleShot(True) timer_callback = functools.partial(self._on_wait_timeout, timer=timer, callback=callback, ) timer.timeout.connect(timer_callback) self.logger.log("waiting %sms; timer %s" % (time_ms, id(timer)), min_level=2) timer.start(time_ms) self._active_timers.add(timer) if onredirect: self._timers_to_cancel_on_redirect[timer] = onredirect if onerror: self._timers_to_cancel_on_error[timer] = onerror def _on_wait_timeout(self, timer, callback): self.logger.log("wait timeout for %s" % id(timer), min_level=2) if timer in self._active_timers: self._active_timers.remove(timer) self._timers_to_cancel_on_redirect.pop(timer, None) self._timers_to_cancel_on_error.pop(timer, None) callback() def _cancel_timer(self, timer, errback=None): self.logger.log("cancelling timer %s" % id(timer), min_level=2) if timer in self._active_timers: self._active_timers.remove(timer) timer.stop() try: if callable(errback): self.logger.log("calling timer errback", min_level=2) errback() finally: timer.deleteLater() def _cancel_timers(self, timers): for timer, oncancel in list(timers.items()): self._cancel_timer(timer, oncancel) timers.pop(timer, None) def _cancel_all_timers(self): self.logger.log("cancelling %d remaining timers" % len(self._active_timers), min_level=2) for timer in list(self._active_timers): self._cancel_timer(timer) def _on_url_changed(self, url): # log history url = unicode(url.toString()) cause_ev = self.web_page.har_log._prev_entry(url, -1) if cause_ev: self._history.append(without_private(cause_ev.data)) self._cancel_timers(self._timers_to_cancel_on_redirect) def run_js_file(self, filename): """ Load JS library from file ``filename`` to the current frame. """ with open(filename, 'rb') as f: script = f.read().decode('utf-8') return self.runjs(script) def run_js_files(self, folder): """ Load all JS libraries from ``folder`` folder to the current frame. """ for jsfile in os.listdir(folder): if jsfile.endswith('.js'): filename = os.path.join(folder, jsfile) self.run_js_file(filename) def autoload(self, js_source): """ Execute JS code before each page load """ self._autoload_scripts.append(js_source) def no_autoload(self): """ Remove all scripts scheduled for auto-loading """ self._autoload_scripts = [] def _on_javascript_window_object_cleared(self): for script in self._autoload_scripts: self.web_page.mainFrame().evaluateJavaScript(script) def http_get(self, url, callback, headers=None, follow_redirects=True): """ Send a GET request; call a callback with the reply as an argument. """ self.http_client.get(url, callback=callback, headers=headers, follow_redirects=follow_redirects ) def runjs(self, js_source): """ Run JS code in page context and return the result. Only string results are supported. """ frame = self.web_page.mainFrame() res = frame.evaluateJavaScript(js_source) return qt2py(res) def store_har_timing(self, name): self.web_page.har_log.store_timing(name) def _jsconsole_enable(self): # TODO: add public interface or make console available by default if self._js_console is not None: return self._js_console = _JavascriptConsole() frame = self.web_page.mainFrame() frame.addToJavaScriptWindowObject('console', self._js_console) def _jsconsole_messages(self): # TODO: add public interface or make console available by default if self._js_console is None: return [] return self._js_console.messages[:] def html(self): """ Return HTML of the current main frame """ self.logger.log("getting HTML", min_level=2) frame = self.web_page.mainFrame() result = bytes(frame.toHtml().toUtf8()) self.store_har_timing("_onHtmlRendered") return result def png(self, width=None, height=None, b64=False): """ Return screenshot in PNG format """ self.logger.log("getting PNG", min_level=2) image = QImage(self.web_page.viewportSize(), QImage.Format_ARGB32) painter = QPainter(image) self.web_page.mainFrame().render(painter) painter.end() self.store_har_timing("_onScreenshotPrepared") if width: image = image.scaledToWidth(width, Qt.SmoothTransformation) if height: image = image.copy(0, 0, width, height) b = QBuffer() image.save(b, "png") result = bytes(b.data()) if b64: result = base64.b64encode(result) self.store_har_timing("_onPngRendered") return result def iframes_info(self, children=True, html=True): """ Return information about all iframes """ self.logger.log("getting iframes", min_level=3) frame = self.web_page.mainFrame() result = self._frame_to_dict(frame, children, html) self.store_har_timing("_onIframesRendered") return result def har(self): """ Return HAR information """ self.logger.log("getting HAR", min_level=3) return self.web_page.har_log.todict() def history(self): """ Return history of 'main' HTTP requests """ self.logger.log("getting history", min_level=3) return copy.deepcopy(self._history) def last_http_status(self): """ Return HTTP status code of the currently loaded webpage or None if it is not available. """ if not self._history: return try: return self._history[-1]["response"]["status"] except KeyError: return def _frame_to_dict(self, frame, children=True, html=True): g = frame.geometry() res = { "url": unicode(frame.url().toString()), "requestedUrl": unicode(frame.requestedUrl().toString()), "geometry": (g.x(), g.y(), g.width(), g.height()), "title": unicode(frame.title()) } if html: res["html"] = unicode(frame.toHtml()) if children: res["childFrames"] = [ self._frame_to_dict(f, True, html) for f in frame.childFrames() ] res["frameName"] = unicode(frame.frameName()) return res
class WebpageRender(object): def __init__(self, network_manager, splash_proxy_factory, splash_request, verbose=False): self.network_manager = network_manager self.web_view = QWebView() self.web_page = SplashQWebPage() self.web_page.setNetworkAccessManager(self.network_manager) self.web_view.setPage(self.web_page) self.web_view.setAttribute(Qt.WA_DeleteOnClose, True) settings = self.web_page.settings() settings.setAttribute(QWebSettings.JavascriptEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, False) settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) settings.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, True) self.web_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.splash_request = splash_request self.web_page.splash_request = splash_request self.web_page.splash_proxy_factory = splash_proxy_factory self.verbose = verbose self.deferred = defer.Deferred() # ======= General request/response handling: def doRequest(self, url, baseurl=None, wait_time=None, viewport=None, js_source=None, js_profile=None, console=False): self.url = url self.wait_time = defaults.WAIT_TIME if wait_time is None else wait_time self.js_source = js_source self.js_profile = js_profile self.console = console self.viewport = defaults.VIEWPORT if viewport is None else viewport request = QNetworkRequest() request.setUrl(QUrl(url.decode('utf8'))) if self.viewport != 'full': # viewport='full' can't be set if content is not loaded yet self._setViewportSize(self.viewport) if getattr(self.splash_request, 'pass_headers', False): headers = self.splash_request.getAllHeaders() for name, value in headers.items(): request.setRawHeader(name, value) if name.lower() == 'user-agent': self.web_page.custom_user_agent = value if baseurl: self._baseUrl = QUrl(baseurl.decode('utf8')) request.setOriginatingObject(self.web_page.mainFrame()) self._reply = self.network_manager.get(request) self._reply.finished.connect(self._requestFinished) else: self.web_page.loadFinished.connect(self._loadFinished) if self.splash_request.method == 'POST': self.web_page.mainFrame().load( request, QNetworkAccessManager.PostOperation, self.splash_request.content.getvalue()) else: self.web_page.mainFrame().load(request) def close(self): self.web_view.stop() self.web_view.close() self.web_page.deleteLater() self.web_view.deleteLater() def _requestFinished(self): self.log("_requestFinished %s" % id(self.splash_request)) self.web_page.loadFinished.connect(self._loadFinished) mimeType = self._reply.header( QNetworkRequest.ContentTypeHeader).toString() data = self._reply.readAll() self.web_page.mainFrame().setContent(data, mimeType, self._baseUrl) if self._reply.error(): log.msg("Error loading %s: %s" % (self.url, self._reply.errorString()), system='render') self._reply.close() self._reply.deleteLater() def _loadFinished(self, ok): self.log("_loadFinished %s" % id(self.splash_request)) if self.deferred.called: # sometimes this callback is called multiple times self.log("_loadFinished called multiple times") return page_ok = ok and self.web_page.errorInfo is None maybe_redirect = not ok and self.web_page.errorInfo is None error_loading = ok and self.web_page.errorInfo is not None if maybe_redirect: self.log("Redirect or other non-fatal error detected %s" % id(self.splash_request)) # XXX: It assumes loadFinished will be called again because # redirect happens. If redirect is detected improperly, # loadFinished won't be called again, and Splash will return # the result only after a timeout. # # FIXME: This can happen if server returned incorrect # Content-Type header; there is no an additional loadFinished # signal in this case. return if page_ok: time_ms = int(self.wait_time * 1000) QTimer.singleShot(time_ms, self._loadFinishedOK) elif error_loading: self.log("loadFinished %s: %s" % (id(self.splash_request), str( self.web_page.errorInfo))) #, min_level=1) # XXX: maybe return a meaningful error page instead of generic # error message? self.deferred.errback(RenderError()) else: self.log("loadFinished %s: unknown error" % id(self.splash_request)) #, min_level=1) self.deferred.errback(RenderError()) def _loadFinishedOK(self): self.log("_loadFinishedOK %s" % id(self.splash_request)) try: self._prerender() self.deferred.callback(self._render()) except: self.deferred.errback() # ======= Rendering methods that subclasses can use: def _getHtml(self): frame = self.web_page.mainFrame() return bytes(frame.toHtml().toUtf8()) def _getPng(self, width=None, height=None): image = QImage(self.web_page.viewportSize(), QImage.Format_ARGB32) painter = QPainter(image) self.web_page.mainFrame().render(painter) painter.end() if width: image = image.scaledToWidth(width, Qt.SmoothTransformation) if height: image = image.copy(0, 0, width, height) b = QBuffer() image.save(b, "png") return bytes(b.data()) def _getIframes(self, children=True, html=True): frame = self.web_page.mainFrame() return self._frameToDict(frame, children, html) def _render(self): raise NotImplementedError() # ======= Other helper methods: def _setViewportSize(self, viewport): w, h = map(int, viewport.split('x')) size = QSize(w, h) self.web_page.setViewportSize(size) def _setFullViewport(self): size = self.web_page.mainFrame().contentsSize() if size.isEmpty(): self.log("contentsSize method doesn't work %s" % id(self.splash_request)) self._setViewportSize(defaults.VIEWPORT_FALLBACK) else: self.web_page.setViewportSize(size) def _loadJsLibs(self, frame, js_profile): if js_profile: for jsfile in os.listdir(js_profile): if jsfile.endswith('.js'): with open(os.path.join(js_profile, jsfile)) as f: frame.evaluateJavaScript(f.read().decode('utf-8')) def _runJS(self, js_source, js_profile): js_output = None js_console_output = None if js_source: frame = self.web_page.mainFrame() if self.console: js_console = JavascriptConsole() frame.addToJavaScriptWindowObject('console', js_console) if js_profile: self._loadJsLibs(frame, js_profile) ret = frame.evaluateJavaScript(js_source) js_output = bytes(ret.toString().toUtf8()) if self.console: js_console_output = [ bytes(s.toUtf8()) for s in js_console.messages ] return js_output, js_console_output def _frameToDict(self, frame, children=True, html=True): g = frame.geometry() res = { "url": unicode(frame.url().toString()), "requestedUrl": unicode(frame.requestedUrl().toString()), "geometry": (g.x(), g.y(), g.width(), g.height()), "title": unicode(frame.title()) } if html: res["html"] = unicode(frame.toHtml()) if children: res["childFrames"] = [ self._frameToDict(f, True, html) for f in frame.childFrames() ] res["frameName"] = unicode(frame.frameName()) return res def _prerender(self): if self.viewport == 'full': self._setFullViewport() self.js_output, self.js_console_output = self._runJS( self.js_source, self.js_profile) def log(self, text): if self.verbose: log.msg(text, system='render')
class WebpageRender(object): """ WebpageRender object renders a webpage: it downloads the page using network_manager and renders it using QWebView according to options passed to :meth:`WebpageRender.doRequest`. This class is not used directly; its subclasses are used. Subclasses choose how to return the result (as html, json, png). """ def __init__(self, network_manager, splash_proxy_factory, splash_request, verbosity): self.network_manager = network_manager self.web_view = QWebView() self.web_page = SplashQWebPage(verbosity) self.web_page.setNetworkAccessManager(self.network_manager) self.web_view.setPage(self.web_page) self.web_view.setAttribute(Qt.WA_DeleteOnClose, True) settings = self.web_page.settings() settings.setAttribute(QWebSettings.JavascriptEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, False) settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) settings.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, True) self.web_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.splash_request = splash_request self.web_page.splash_request = splash_request self.web_page.splash_proxy_factory = splash_proxy_factory self.verbosity = verbosity self.deferred = defer.Deferred() self._finished_timer = None self._closing = False # ======= General request/response handling: def start(self, url, baseurl=None, wait=None, viewport=None, js_source=None, js_profile=None, images=None, console=False, headers=None, http_method='GET', body=None): self.web_page.har_log.store_timing("_onStarted") self.url = url self.history = [] self.web_page.settings().setAttribute(QWebSettings.AutoLoadImages, images) self.wait_time = defaults.WAIT_TIME if wait is None else wait self.js_source = js_source self.js_profile = js_profile self.console = console self.viewport = defaults.VIEWPORT if viewport is None else viewport # setup logging if self.verbosity >= 4: self.web_page.loadStarted.connect(self._loadStarted) self.web_page.mainFrame().loadFinished.connect(self._frameLoadFinished) self.web_page.mainFrame().loadStarted.connect(self._frameLoadStarted) self.web_page.mainFrame().contentsSizeChanged.connect(self._contentsSizeChanged) if self.verbosity >= 3: self.web_page.mainFrame().javaScriptWindowObjectCleared.connect(self._javaScriptWindowObjectCleared) self.web_page.mainFrame().initialLayoutCompleted.connect(self._initialLayoutCompleted) self.web_page.mainFrame().urlChanged.connect(self._urlChanged) # do the request request = QNetworkRequest() request.setUrl(QUrl(url.decode('utf8'))) self._setHeaders(request, headers) if getattr(self.splash_request, 'inspect_me', False): # Set http method and request body from the request http_method = self.splash_request.method body = self.splash_request.content.getvalue() if self.viewport != 'full': # viewport='full' can't be set if content is not loaded yet, # but in other cases it is better to set it earlier. self._setViewportSize(self.viewport) if baseurl: # If baseurl is used, we download the page manually, # then set its contents to the QWebPage and let it # download related resources and render the result. if http_method != 'GET': raise NotImplementedError() self._baseUrl = QUrl(baseurl.decode('utf8')) request.setOriginatingObject(self.web_page.mainFrame()) self._reply = self.network_manager.get(request) self._reply.finished.connect(self._requestFinished) else: self.web_page.loadFinished.connect(self._loadFinished) meth = OPERATION_QT_CONSTANTS[http_method] if body is None: # PyQT doesn't support body=None self.web_page.mainFrame().load(request, meth) else: self.web_page.mainFrame().load(request, meth, body) def render(self): """ This method is called to get the result after the requested page is downloaded and rendered. Subcalles should implement it to customize which data to return. """ raise NotImplementedError() def close(self): """ This method is called by a Pool after the rendering is done and the WebpageRender object is no longer needed. """ self._closing = True self.web_view.pageAction(QWebPage.StopScheduledPageRefresh) self.web_view.stop() self.web_view.close() self.web_page.deleteLater() self.web_view.deleteLater() def _setHeaders(self, request, headers): """ Set HTTP headers for the ``request``. """ if isinstance(headers, dict): headers = headers.items() for name, value in headers or []: request.setRawHeader(name, value) if name.lower() == 'user-agent': self.web_page.custom_user_agent = value def _requestFinished(self): """ This method is called when ``baseurl`` is used and a reply for the first request is received. """ self.log("_requestFinished %s" % id(self.splash_request)) self.web_page.loadFinished.connect(self._loadFinished) mimeType = self._reply.header(QNetworkRequest.ContentTypeHeader).toString() data = self._reply.readAll() self.web_page.mainFrame().setContent(data, mimeType, self._baseUrl) if self._reply.error(): self.log("Error loading %s: %s" % (self.url, self._reply.errorString()), min_level=1) self._reply.close() self._reply.deleteLater() def _loadFinished(self, ok): """ This method is called when a QWebPage finished loading its contents. """ if self._closing: self.log("loadFinished is ignored because WebpageRender is closing", min_level=3) return if self.deferred.called: # sometimes this callback is called multiple times self.log("loadFinished called multiple times", min_level=1) return page_ok = ok and self.web_page.errorInfo is None maybe_redirect = not ok and self.web_page.errorInfo is None error_loading = ok and self.web_page.errorInfo is not None if maybe_redirect: self.log("Redirect or other non-fatal error detected %s" % id(self.splash_request)) # XXX: It assumes loadFinished will be called again because # redirect happens. If redirect is detected improperly, # loadFinished won't be called again, and Splash will return # the result only after a timeout. # # FIXME: This can happen if server returned incorrect # Content-Type header; there is no an additional loadFinished # signal in this case. return if page_ok: # or maybe_redirect: if self.wait_time == 0: self.log("loadFinished %s; not waiting" % (id(self.splash_request))) self._loadFinishedOK() else: time_ms = int(self.wait_time * 1000) self.log("loadFinished %s; waiting %sms" % (id(self.splash_request), time_ms)) if self._finished_timer is not None: raise Exception("timer is not None!") self._finished_timer = QTimer() self._finished_timer.setSingleShot(True) self._finished_timer.timeout.connect(self._loadFinishedOK) self._finished_timer.start(time_ms) elif error_loading: self.log("loadFinished %s: %s" % (id(self.splash_request), str(self.web_page.errorInfo)), min_level=1) # XXX: maybe return a meaningful error page instead of generic # error message? self.deferred.errback(RenderError()) else: self.log("loadFinished %s: unknown error" % id(self.splash_request), min_level=1) self.deferred.errback(RenderError()) def _loadFinishedOK(self): self._finished_timer = None self.log("_loadFinishedOK %s" % id(self.splash_request)) if self._closing: self.log("loadFinishedOK is ignored because WebpageRender is closing", min_level=3) return self.web_view.pageAction(QWebPage.StopScheduledPageRefresh) self.web_view.stop() self.web_page.har_log.store_timing("_onPrepareStart") try: self._prepareRender() self.deferred.callback(self.render()) except: self.deferred.errback() def _frameLoadFinished(self, ok): self.log("mainFrame().LoadFinished %s %s" % (id(self.splash_request), ok), min_level=4) def _loadStarted(self): self.log("loadStarted %s" % id(self.splash_request), min_level=4) def _urlChanged(self, url): cause_ev = self.web_page.har_log._prev_entry(unicode(url.toString()), -1) if cause_ev: self.history.append(without_private(cause_ev.data)) msg = "mainFrame().urlChanged %s: %s" % (id(self.splash_request), qurl2ascii(url)) self.log(msg, min_level=3) if self._finished_timer is not None: self.log("Cancelling wait timer %s" % (id(self.splash_request))) self._finished_timer.stop() self._finished_timer = None def _frameLoadStarted(self): self.log("mainFrame().loadStarted %s" % id(self.splash_request), min_level=4) def _initialLayoutCompleted(self): self.log("mainFrame().initialLayoutCompleted %s" % id(self.splash_request), min_level=3) def _javaScriptWindowObjectCleared(self): self.log("mainFrame().javaScriptWindowObjectCleared %s" % id(self.splash_request), min_level=3) def _contentsSizeChanged(self): self.log("mainFrame().contentsSizeChanged %s" % id(self.splash_request), min_level=4) def _repaintRequested(self): self.log("mainFrame().repaintRequested %s" % id(self.splash_request), min_level=4) # ======= Rendering methods that subclasses can use: def _getHtml(self): self.log("getting HTML %s" % id(self.splash_request)) frame = self.web_page.mainFrame() result = bytes(frame.toHtml().toUtf8()) self.web_page.har_log.store_timing("_onHtmlRendered") return result def _getPng(self, width=None, height=None, b64=False): self.log("getting PNG %s" % id(self.splash_request)) image = QImage(self.web_page.viewportSize(), QImage.Format_ARGB32) painter = QPainter(image) self.web_page.mainFrame().render(painter) painter.end() self.web_page.har_log.store_timing("_onScreenshotPrepared") if width: image = image.scaledToWidth(width, Qt.SmoothTransformation) if height: image = image.copy(0, 0, width, height) b = QBuffer() image.save(b, "png") result = bytes(b.data()) if b64: result = base64.b64encode(result) self.web_page.har_log.store_timing("_onPngRendered") return result def _getIframes(self, children=True, html=True): self.log("getting iframes %s" % id(self.splash_request), min_level=3) frame = self.web_page.mainFrame() result = self._frameToDict(frame, children, html) self.web_page.har_log.store_timing("_onIframesRendered") return result def _getHistory(self): self.log("getting history %s" % id(self.splash_request), min_level=3) hist = copy.deepcopy(self.history) for entry in hist: if entry is not None: del entry['request']['queryString'] return hist def _getHAR(self): self.log("getting HAR %s" % id(self.splash_request), min_level=3) return self.web_page.har_log.todict() # ======= Other helper methods: def _setViewportSize(self, size): if not isinstance(size, QSize): w, h = map(int, size.split('x')) size = QSize(w, h) self.web_page.setViewportSize(size) w, h = int(size.width()), int(size.height()) self.log("viewport size for %s is set to %sx%s" % (id(self.splash_request), w, h)) def _setFullViewport(self): size = self.web_page.mainFrame().contentsSize() if size.isEmpty(): self.log("contentsSize method doesn't work %s" % id(self.splash_request), min_level=1) self._setViewportSize(defaults.VIEWPORT_FALLBACK) else: self._setViewportSize(size) self.web_page.har_log.store_timing("_onFullViewportSet") def _loadJsLibs(self, frame, js_profile): if js_profile: for jsfile in os.listdir(js_profile): if jsfile.endswith('.js'): with open(os.path.join(js_profile, jsfile)) as f: frame.evaluateJavaScript(f.read().decode('utf-8')) def _runJS(self, js_source, js_profile): js_output = None js_console_output = None if js_source: frame = self.web_page.mainFrame() if self.console: js_console = JavascriptConsole() frame.addToJavaScriptWindowObject('console', js_console) if js_profile: self._loadJsLibs(frame, js_profile) ret = frame.evaluateJavaScript(js_source) js_output = bytes(ret.toString().toUtf8()) if self.console: js_console_output = [bytes(s.toUtf8()) for s in js_console.messages] self.web_page.har_log.store_timing('_onCustomJsExecuted') return js_output, js_console_output def _frameToDict(self, frame, children=True, html=True): g = frame.geometry() res = { "url": unicode(frame.url().toString()), "requestedUrl": unicode(frame.requestedUrl().toString()), "geometry": (g.x(), g.y(), g.width(), g.height()), "title": unicode(frame.title()) } if html: res["html"] = unicode(frame.toHtml()) if children: res["childFrames"] = [self._frameToDict(f, True, html) for f in frame.childFrames()] res["frameName"] = unicode(frame.frameName()) return res def _prepareRender(self): if self.viewport == 'full': self._setFullViewport() self.js_output, self.js_console_output = self._runJS(self.js_source, self.js_profile) def log(self, text, min_level=2): if self.verbosity >= min_level: if isinstance(text, unicode): text = text.encode('unicode-escape').decode('ascii') log.msg(text, system='render')
class WebpageRender(object): def __init__(self, network_manager): self.network_manager = network_manager self.web_view = QWebView() self.web_page = SplashQWebPage() self.web_page.setNetworkAccessManager(self.network_manager) self.web_view.setPage(self.web_page) self.web_view.setAttribute(Qt.WA_DeleteOnClose, True) settings = self.web_view.settings() settings.setAttribute(QWebSettings.JavascriptEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, False) settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) self.web_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) def doRequest(self, url, baseurl=None, wait_time=None): self.url = url self.wait_time = defaults.WAIT_TIME if wait_time is None else wait_time self.deferred = defer.Deferred() request = QNetworkRequest() request.setUrl(QUrl(url)) if baseurl: self._baseUrl = QUrl(baseurl) self.network_manager.finished.connect(self._requestFinished) self.network_manager.get(request) else: self.web_page.loadFinished.connect(self._loadFinished) self.web_page.mainFrame().load(request) def close(self): self.web_view.stop() self.web_view.close() self.web_page.deleteLater() self.web_view.deleteLater() self.network_manager.deleteLater() def _requestFinished(self, reply): self.web_page.networkAccessManager().finished.disconnect(self._requestFinished) self.web_view.loadFinished.connect(self._loadFinished) mimeType = reply.header(QNetworkRequest.ContentTypeHeader).toString() self.web_view.page().mainFrame().setContent(reply.readAll(), mimeType, self._baseUrl) def _loadFinished(self, ok): if self.deferred.called: return if ok: time_ms = int(self.wait_time * 1000) QTimer.singleShot(time_ms, self._loadFinishedOK) else: self.deferred.errback(RenderError()) def _loadFinishedOK(self): if self.deferred.called: return try: self.deferred.callback(self._render()) except: self.deferred.errback() def _getHtml(self): frame = self.web_view.page().mainFrame() return bytes(frame.toHtml().toUtf8()) def _getPng(self, width=None, height=None, viewport=None): self._setViewportSize(viewport) image = QImage(self.web_page.viewportSize(), QImage.Format_ARGB32) painter = QPainter(image) self.web_page.mainFrame().render(painter) painter.end() if width: image = image.scaledToWidth(width, Qt.SmoothTransformation) if height: image = image.copy(0, 0, width, height) b = QBuffer() image.save(b, "png") return bytes(b.data()) def _getIframes(self, children=True, html=True): frame = self.web_view.page().mainFrame() return self._frameToDict(frame, children, html) def _setViewportSize(self, viewport=None): viewport = defaults.VIEWPORT if viewport is None else viewport if viewport == 'full': size = self.web_page.mainFrame().contentsSize() if size.isEmpty(): # sometimes contentsSize doesn't work w, h = map(int, defaults.VIEWPORT_FALLBACK.split('x')) size = QSize(w, h) else: w, h = map(int, viewport.split('x')) size = QSize(w, h) self.web_page.setViewportSize(size) def _frameToDict(self, frame, children=True, html=True): g = frame.geometry() res = { "url": unicode(frame.url().toString()), "requestedUrl": unicode(frame.requestedUrl().toString()), "geometry": (g.x(), g.y(), g.width(), g.height()), "title": unicode(frame.title()) } if html: res["html"] = unicode(frame.toHtml()) if children: res["childFrames"] = [self._frameToDict(f, True, html) for f in frame.childFrames()] res["frameName"] = unicode(frame.frameName()) return res def _render(self): raise NotImplementedError()
class BrowserTab(object): """ An object for controlling a single browser tab (QWebView). It is created by splash.pool.Pool. Pool attaches to tab's deferred and waits until either a callback or an errback is called, then destroys a BrowserTab. """ def __init__(self, network_manager, splash_proxy_factory, verbosity, render_options): """ Create a new browser tab. """ self.deferred = defer.Deferred() self.network_manager = network_manager self.verbosity = verbosity self._uid = render_options.get_uid() self._closing = False self._default_headers = None self._active_timers = set() self._timers_to_cancel_on_redirect = weakref.WeakKeyDictionary( ) # timer: callback self._timers_to_cancel_on_error = weakref.WeakKeyDictionary( ) # timer: callback self._js_console = None self._history = [] self._init_webpage(verbosity, network_manager, splash_proxy_factory, render_options) self._setup_logging(verbosity) def _init_webpage(self, verbosity, network_manager, splash_proxy_factory, render_options): """ Create and initialize QWebPage and QWebView """ self.web_page = SplashQWebPage(verbosity) self.web_page.setNetworkAccessManager(network_manager) self.web_page.splash_proxy_factory = splash_proxy_factory self.web_page.render_options = render_options self._set_default_webpage_options(self.web_page) self._setup_webpage_events() self.web_view = QWebView() self.web_view.setPage(self.web_page) self.web_view.setAttribute(Qt.WA_DeleteOnClose, True) def _set_default_webpage_options(self, web_page): """ Set QWebPage options. TODO: allow to customize them. """ settings = web_page.settings() settings.setAttribute(QWebSettings.JavascriptEnabled, True) settings.setAttribute(QWebSettings.PluginsEnabled, False) settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True) settings.setAttribute(QWebSettings.LocalStorageEnabled, True) settings.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, True) web_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) def _setup_logging(self, verbosity): """ Setup logging of various events """ self.logger = _BrowserTabLogger( uid=self._uid, web_page=self.web_page, verbosity=verbosity, ) self.logger.enable() def _setup_webpage_events(self): self._load_finished = WrappedSignal( self.web_page.mainFrame().loadFinished) self.web_page.mainFrame().loadFinished.connect(self._on_load_finished) self.web_page.mainFrame().urlChanged.connect(self._on_url_changed) def return_result(self, result): """ Return a result to the Pool. """ if self.result_already_returned(): self.logger.log("error: result is already returned", min_level=1) self.deferred.callback(result) # self.deferred = None def return_error(self, error=None): """ Return an error to the Pool. """ if self.result_already_returned(): self.logger.log("error: result is already returned", min_level=1) self.deferred.errback(error) # self.deferred = None def result_already_returned(self): """ Return True if an error or a result is already returned to Pool """ return self.deferred.called def set_default_headers(self, headers): """ Set default HTTP headers """ self._default_headers = headers def set_images_enabled(self, enabled): self.web_page.settings().setAttribute(QWebSettings.AutoLoadImages, enabled) def set_viewport(self, size): """ Set viewport size. If size is "full" viewport size is detected automatically. If can also be "<width>x<height>". """ if size == 'full': size = self.web_page.mainFrame().contentsSize() if size.isEmpty(): self.logger.log("contentsSize method doesn't work %s", min_level=1) size = defaults.VIEWPORT_FALLBACK if not isinstance(size, QSize): w, h = map(int, size.split('x')) size = QSize(w, h) self.web_page.setViewportSize(size) w, h = int(size.width()), int(size.height()) self.logger.log("viewport size is set to %sx%s" % (w, h), min_level=2) return w, h @property def url(self): """ Current URL """ return unicode(self.web_page.mainFrame().url().toString()) def go(self, url, callback, errback, baseurl=None, http_method='GET', body=None): """ Go to an URL. This is similar to entering an URL in address tab and pressing Enter. """ self.store_har_timing("_onStarted") if baseurl: # If baseurl is used, we download the page manually, # then set its contents to the QWebPage and let it # download related resources and render the result. if http_method != 'GET': raise NotImplementedError() request = self._create_request(url) request.setOriginatingObject(self.web_page.mainFrame()) # TODO / FIXME: add support for multiple replies # or discard/cancel previous replies self._reply = self.network_manager.get(request) cb = functools.partial( self._on_baseurl_request_finished, callback=callback, errback=errback, baseurl=baseurl, url=url, ) self._reply.finished.connect(cb) else: # if not self._goto_callbacks.isempty(): # self.logger.log("Only a single concurrent 'go' request is supported. " # "Previous go requests will be cancelled.", min_level=1) # # When a new URL is loaded to mainFrame an errback will # # be called, so we're not cancelling this callback manually. callback_id = self._load_finished.connect( self._on_goto_load_finished, callback=callback, errback=errback, ) self.logger.log("callback %s is connected to loadFinished" % callback_id, min_level=3) self._load_url_to_mainframe(url, http_method, body) def stop_loading(self): """ Stop loading of the current page and all pending page refresh/redirect requests. """ self.logger.log("stop_loading", min_level=2) self.web_view.pageAction(QWebPage.StopScheduledPageRefresh) self.web_view.stop() def _close(self): """ Destroy this tab """ self._closing = True self.web_view.pageAction(QWebPage.StopScheduledPageRefresh) self.web_view.stop() self.web_view.close() self.web_page.deleteLater() self.web_view.deleteLater() self._cancel_all_timers() @skip_if_closing def _on_load_finished(self, ok): if self.web_page.maybe_redirect(ok): self.logger.log("Redirect or other non-fatal error detected", min_level=2) return if self.web_page.is_ok(ok): # or maybe_redirect: self.logger.log("loadFinished: ok", min_level=2) else: self._cancel_timers(self._timers_to_cancel_on_error) if self.web_page.error_loading(ok): self.logger.log("loadFinished: %s" % (str(self.web_page.error_info)), min_level=1) else: self.logger.log("loadFinished: unknown error", min_level=1) def _on_baseurl_request_finished(self, callback, errback, baseurl, url): """ This method is called when ``baseurl`` is used and a reply for the first request is received. """ self.logger.log("baseurl_request_finished", min_level=2) callback_id = self._load_finished.connect( self._on_goto_load_finished, callback=callback, errback=errback, ) self.logger.log("callback %s is connected to loadFinished" % callback_id, min_level=3) baseurl = QUrl(baseurl) mimeType = self._reply.header( QNetworkRequest.ContentTypeHeader).toString() data = self._reply.readAll() self.web_page.mainFrame().setContent(data, mimeType, baseurl) if self._reply.error(): self.logger.log("Error loading %s: %s" % (url, self._reply.errorString()), min_level=1) self._reply.close() self._reply.deleteLater() def _load_url_to_mainframe(self, url, http_method, body=None): request = self._create_request(url) meth = OPERATION_QT_CONSTANTS[http_method] if body is None: # PyQT doesn't support body=None self.web_page.mainFrame().load(request, meth) else: self.web_page.mainFrame().load(request, meth, body) def _create_request(self, url): request = QNetworkRequest() request.setUrl(QUrl(url)) self._set_request_headers(request, self._default_headers) return request @skip_if_closing def _on_goto_load_finished(self, ok, callback, errback, callback_id): """ This method is called when a QWebPage finishes loading its contents. """ if self.web_page.maybe_redirect(ok): # XXX: It assumes loadFinished will be called again because # redirect happens. If redirect is detected improperly, # loadFinished won't be called again, and Splash will return # the result only after a timeout. return self.logger.log("loadFinished: disconnecting callback %s" % callback_id, min_level=3) self._load_finished.disconnect(callback_id) if self.web_page.is_ok(ok): callback() elif self.web_page.error_loading(ok): # XXX: maybe return a meaningful error page instead of generic # error message? errback() # errback(RenderError()) else: errback() # errback(RenderError()) def _set_request_headers(self, request, headers): """ Set HTTP headers for the request. """ if isinstance(headers, dict): headers = headers.items() for name, value in headers or []: request.setRawHeader(name, value) if name.lower() == 'user-agent': self.web_page.custom_user_agent = value def wait(self, time_ms, callback, onredirect=None, onerror=None): """ Wait for time_ms, then run callback. If onredirect is True then the timer is cancelled if redirect happens. If onredirect is callable then in case of redirect the timer is cancelled and this callable is called. If onerror is True then the timer is cancelled if a render error happens. If onerror is callable then in case of a render error the timer is cancelled and this callable is called. """ timer = QTimer() timer.setSingleShot(True) timer_callback = functools.partial( self._on_wait_timeout, timer=timer, callback=callback, ) timer.timeout.connect(timer_callback) self.logger.log("waiting %sms; timer %s" % (time_ms, id(timer)), min_level=2) timer.start(time_ms) self._active_timers.add(timer) if onredirect: self._timers_to_cancel_on_redirect[timer] = onredirect if onerror: self._timers_to_cancel_on_error[timer] = onerror def _on_wait_timeout(self, timer, callback): self.logger.log("wait timeout for %s" % id(timer), min_level=2) if timer in self._active_timers: self._active_timers.remove(timer) self._timers_to_cancel_on_redirect.pop(timer, None) self._timers_to_cancel_on_error.pop(timer, None) callback() def _cancel_timer(self, timer, errback=None): self.logger.log("cancelling timer %s" % id(timer), min_level=2) if timer in self._active_timers: self._active_timers.remove(timer) timer.stop() if callable(errback): self.logger.log("calling timer errback", min_level=2) errback() timer.deleteLater() def _cancel_timers(self, timers): for timer, oncancel in list(timers.items()): self._cancel_timer(timer, oncancel) timers.pop(timer, None) def _cancel_all_timers(self): self.logger.log("cancelling %d remaining timers" % len(self._active_timers), min_level=2) for timer in list(self._active_timers): self._cancel_timer(timer) def _on_url_changed(self, url): # log history url = unicode(url.toString()) cause_ev = self.web_page.har_log._prev_entry(url, -1) if cause_ev: self._history.append(without_private(cause_ev.data)) self._cancel_timers(self._timers_to_cancel_on_redirect) def inject_js(self, filename): """ Load JS library from file ``filename`` to the current frame. """ # TODO: shouldn't it keep injected scripts after redirects/reloads? with open(filename, 'rb') as f: script = f.read().decode('utf-8') return self.runjs(script) def inject_js_libs(self, folder): """ Load all JS libraries from ``folder`` folder to the current frame. """ # TODO: shouldn't it keep injected scripts after redirects/reloads? for jsfile in os.listdir(folder): if jsfile.endswith('.js'): filename = os.path.join(folder, jsfile) self.inject_js(filename) def runjs(self, js_source): """ Run JS code in page context and return the result. Only string results are supported. """ frame = self.web_page.mainFrame() res = frame.evaluateJavaScript(js_source) return qt2py(res) def store_har_timing(self, name): self.web_page.har_log.store_timing(name) def _jsconsole_enable(self): # TODO: add public interface or make console available by default if self._js_console is not None: return self._js_console = _JavascriptConsole() frame = self.web_page.mainFrame() frame.addToJavaScriptWindowObject('console', self._js_console) def _jsconsole_messages(self): # TODO: add public interface or make console available by default if self._js_console is None: return [] return self._js_console.messages[:] def html(self): """ Return HTML of the current main frame """ self.logger.log("getting HTML", min_level=2) frame = self.web_page.mainFrame() result = bytes(frame.toHtml().toUtf8()) self.store_har_timing("_onHtmlRendered") return result def png(self, width=None, height=None, b64=False): """ Return screenshot in PNG format """ self.logger.log("getting PNG", min_level=2) image = QImage(self.web_page.viewportSize(), QImage.Format_ARGB32) painter = QPainter(image) self.web_page.mainFrame().render(painter) painter.end() self.store_har_timing("_onScreenshotPrepared") if width: image = image.scaledToWidth(width, Qt.SmoothTransformation) if height: image = image.copy(0, 0, width, height) b = QBuffer() image.save(b, "png") result = bytes(b.data()) if b64: result = base64.b64encode(result) self.store_har_timing("_onPngRendered") return result def iframes_info(self, children=True, html=True): """ Return information about all iframes """ self.logger.log("getting iframes", min_level=3) frame = self.web_page.mainFrame() result = self._frame_to_dict(frame, children, html) self.store_har_timing("_onIframesRendered") return result def har(self): """ Return HAR information """ self.logger.log("getting HAR", min_level=3) return self.web_page.har_log.todict() def history(self): """ Return history of 'main' HTTP requests """ self.logger.log("getting history", min_level=3) return copy.deepcopy(self._history) def last_http_status(self): """ Return HTTP status code of the currently loaded webpage or None if it is not available. """ if not self._history: return try: return self._history[-1]["response"]["status"] except KeyError: return def _frame_to_dict(self, frame, children=True, html=True): g = frame.geometry() res = { "url": unicode(frame.url().toString()), "requestedUrl": unicode(frame.requestedUrl().toString()), "geometry": (g.x(), g.y(), g.width(), g.height()), "title": unicode(frame.title()) } if html: res["html"] = unicode(frame.toHtml()) if children: res["childFrames"] = [ self._frame_to_dict(f, True, html) for f in frame.childFrames() ] res["frameName"] = unicode(frame.frameName()) return res
class sau_edit(qt.QMainWindow): def __init__(self): super(sau_edit, self).__init__() self.screen = qt.QDesktopWidget().screenGeometry() self.ht = (self.screen.height()) self.wd = (self.screen.width()) self.setGeometry(0, 0, self.wd, self.ht) self.setMinimumSize(self.wd / 2, self.ht / 2) #self.setGeometry((self.screen.width()-self.wd)/2,0,self.wd,self.ht-50) #self.setFixedSize(self.wd,self.ht-50) #self.showMaximized() #self.showFullScreen() #self.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.setWindowTitle("SAU-editor") self.setWindowIcon(qt.QIcon("notepad.png")) #---------------------------------status label------------------------------------- """self.status=qt.QLabel(self) self.status.move(750,500)#self.wd-200,self.ht-200) self.status.setStyleSheet("QTextEdit {color:red;background-color:white}")""" #---------------------------------all menu bar entry-------------------------------- new_file_key = qt.QAction("&New file", self) new_file_key.setShortcut("Ctrl+n") new_file_key.setStatusTip('New file') new_file_key.triggered.connect(self.new_file) new_window_file_key = qt.QAction("&New window", self) new_window_file_key.setShortcut("Ctrl+Shift+n") new_window_file_key.setStatusTip('New window') new_window_file_key.triggered.connect(self.new_window_file) open_file_key = qt.QAction("&Open file", self) open_file_key.setShortcut("Ctrl+o") open_file_key.setStatusTip('Open file') open_file_key.triggered.connect(self.open_file) save_key = qt.QAction("&Save", self) save_key.setShortcut("Ctrl+s") save_key.setStatusTip('Save file') save_key.triggered.connect(self.save_file) quit_key = qt.QAction("&Quit application !", self) quit_key.setShortcut("Ctrl+Q") quit_key.setStatusTip('Leave The App') quit_key.triggered.connect(self.close_application) copy_key = qt.QAction("&Copy", self) copy_key.setShortcut("Ctrl+c") copy_key.setStatusTip('copy') copy_key.triggered.connect(self.save_file) cut_key = qt.QAction("&Cut", self) cut_key.setShortcut("Ctrl+x") cut_key.setStatusTip('cut') cut_key.triggered.connect(self.save_file) paste_key = qt.QAction("&Paste", self) paste_key.setShortcut("Ctrl+v") paste_key.setStatusTip('paste') paste_key.triggered.connect(self.save_file) about_key = qt.QAction("&About", self) about_key.setStatusTip('About editor') about_key.triggered.connect(self.about_info) terminal_key = qt.QAction("&Terminal", self) terminal_key.setStatusTip('Open terminal') terminal_key.triggered.connect(self.show_terminal) refresh_key = qt.QAction("&Refresh", self) refresh_key.setStatusTip('Refresh html page') refresh_key.triggered.connect(self.refresh_fun) self.statusBar() self.terminal() #self.terminal1() #-------------------------------------all menu-s------------------------------------- mainMenu = self.menuBar() fileMenu = mainMenu.addMenu('&File') fileMenu.addAction(new_file_key) fileMenu.addAction(new_window_file_key) fileMenu.addAction(open_file_key) fileMenu.addAction(save_key) fileMenu.addAction(quit_key) edit_menu = mainMenu.addMenu('&Edit') edit_menu.addAction(copy_key) edit_menu.addAction(cut_key) edit_menu.addAction(paste_key) tool_menu = mainMenu.addMenu('&Tool') tool_menu.addAction(terminal_key) tool_menu.addAction(refresh_key) help_menu = mainMenu.addMenu('&Help') help_menu.addAction(about_key) #-------------------------------------textEdit----------------------------------------- self.text_edit_field = QsciScintilla(self) self.text_edit_field.setLexer(Qsci.QsciLexerCPP(self)) self.text_edit_field.setMarginType(10, QsciScintilla.NumberMargin) self.text_edit_field.setMarginWidth(0, "0000") self.text_edit_field.setStyleSheet( "QsciScintilla {background-color:red}") self.text_edit_field.setIndentationGuides(True) self.text_edit_field.setAutoIndent(True) self.text_edit_field.setAutoCompletionThreshold(1) self.text_edit_field.setAutoCompletionSource(QsciScintilla.AcsDocument) self.text_edit_field.setCallTipsStyle( QsciScintilla.CallTipsNoAutoCompletionContext) #self.text_edit_field.setCaretLineVisible(True) self.text_edit_field.setFolding(QsciScintilla.BoxedTreeFoldStyle) self.text_edit_field.setMarginsBackgroundColor(QColor("#333333")) #self.text_edit_field.setCaretLineBackgroundColor(QColor("#A9A9A9")) #self.text_edit_field.setFixedSize(self.wd-500,self.ht-80) self.text_edit_field.setMaximumWidth(300) try: self.text_edit_field.setMinimumSize(self.wd / 2, self.ht / 2) except: pass #self.text_edit_field.showMaximized() self.text_edit_field.move(0, 30) self.font = qt.QFont() self.font.setFamily('Courier') self.font.setPointSize(10) self.text_edit_field.setFont(self.font) #self.text_edit_field.setFontItalic(True) self.text_edit_field.setStyleSheet( "QsciScintilla {color:green;background-color:black}") #highlight = syntax.PythonHighlighter(self.text_edit_field.document()) self.text_edit_field.show() self.file_datail() """try: with open("last_opened.file","r") as lo: self.open_file(lo.read()) print( str(lo.read())) except: pass""" self.load_previous_file() self.show() #-------------------------------------functions----------------------------------- def load_previous_file(self): with open("last_opened.file", "r") as lof: f = lof.read() print(lof.read().strip()) if "." in f: with open(f) as data: cont = data.read() self.text_edit_field.setText(cont) self.file_name = f def file_datail(self): self.file_name_title = qt.QLabel("name:\t", self) self.file_name_title.move(710, 350) self.file_name_title.setFixedSize(250, 30) self.file_size = qt.QLabel("size:\t", self) self.file_size.move(710, 370) self.file_size.setFixedSize(250, 30) def new_window_file(self): os.system("python text_editor.py") def show_terminal(self): path = os.getcwd() os.system("xterm") def terminal(self): self.process = QProcess(self) self.terminal = QWidget(self) #layout = QVBoxLayout(self) #layout.addWidget(self.terminal) self.process.start('xterm', ['-into', str(self.terminal.winId())]) self.terminal.move(self.get_window_size()[0] * 0.66, 30) self.terminal.setFixedSize(490, 600) def terminal1(self): self.process = QProcess(self) self.terminal = QWidget(self) #layout = QVBoxLayout(self) #layout.addWidget(self.terminal) self.process.start('xterm', ['-into', str(self.terminal.winId())]) self.terminal.move(700, 350) self.terminal.setFixedSize(490, 600) def syn(self): data = open(self.file_name, "r").read() data = data.replace("int", "<font color='red'>int</font>") return data def resizeEvent(self, event): #print(("resize")) self.x_y = self.get_window_size() print(self.x_y) self.set_type() if self.x_y[0] <= self.wd / 2: self.terminal.setHidden(True) self.browser.move(self.x_y[0], 30) self.browser.setFixedSize(self.x_y[0] * 0.66, 30) self.file_name_title.setHidden(True) self.file_size.setHidden(True) self.text_edit_field.setFixedSize(self.x_y[0], self.x_y[1]) elif self.x_y[0] >= self.wd / 2: self.terminal.show() self.text_edit_field.setFixedSize(self.x_y[0] * .666, self.x_y[1] - 20) self.file_name_title.move((self.x_y[0] * 0.66) + 10, 350) self.file_size.move((self.x_y[0] * 0.66) + 10, 370) self.terminal.move(self.x_y[0] * 0.66, 30) self.terminal.setFixedSize(self.x_y[0] * 0.33, 600) def get_window_size(self): data = str(self.size()).split("PyQt4.QtCore.QSize")[1].split(",") data1 = [] data1.append(int(data[0][1:])) data1.append(int(data[1][1:-1])) return data1 def about_info(self): self.o = about() self.o.show() def close_application(self): try: with open("last_opened.file", "w") as lo: self.write(self.file_name) print("name stored") except: pass choice = qt.QMessageBox.question( self, "Confirm Exit...", "Are you sure you want to exit ?", qt.QMessageBox.Yes | qt.QMessageBox.No) if choice == qt.QMessageBox.Yes: sys.exit() else: pass def new_file(self): print("new file") self.save_file() self.file_name = None self.text_edit_field.setText("") self.browser.setHidden(True) self.terminal.setHidden(False) def open_file(self): self.fileDialog = qt.QFileDialog(self) self.file_name = self.fileDialog.getOpenFileName() self.setWindowTitle("sau-edit: " + self.file_name) try: with open(self.file_name) as f: self.text_edit_field.setText(f.read()) except: pass #print( self.text_edit_field.text()) #self.text_edit_field.appendHtml(f.read()) #self.text_edit_field.appendHtml(self.syn()) self.file_name_title.setText("<font color='red'>name:</font>" + self.file_name.split("/")[-1]) self.file_size.setText("<font color='red'>size:</font>" + str(os.path.getsize(self.file_name)) + " bytes") self.set_type() """def open_file(self,last_file_name): #self.setWindowTitle("sau-edit: "+last_file_name) if last_file_name==None: print( "none") with open(last_file_name) as f: self.text_edit_field.setText(f.read()) #print( self.text_edit_field.text()) #self.text_edit_field.appendHtml(f.read()) #self.text_edit_field.appendHtml(self.syn()) self.file_name_title.setText("<font color='red'>name:</font>"+self.file_name.split("/")[-1]) self.file_size.setText("<font color='red'>size:</font>"+str(os.path.getsize(self.file_name))+" bytes")""" def save_file(self): print("saved") self.set_type() self.hide_web() try: with open(self.file_name, "w") as f: f.write(self.text_edit_field.text()) print("successful") self.file_name_title.setText("<font color='red'>name:</font>" + self.file_name.split("/")[-1]) self.file_size.setText("<font color='red'>size:</font>" + str(os.path.getsize(self.file_name)) + " bytes") self.setWindowTitle("Sau's Editor: " + str(self.file_name)) except: try: name = qt.QFileDialog.getSaveFileName(self, 'Save File') file = open(name, 'w') text = self.text_edit_field.text() file.write(text) file.close() self.file_name = name self.file_name_title.setText("<font color='red'>name:</font>" + self.file_name.split("/")[-1]) self.file_size.setText("<font color='red'>size:</font>" + str(os.path.getsize(self.file_name)) + " bytes") except: pass #self.status.setText("saved") def closeEvent(self, event): print(event) result = qt.QMessageBox.question( self, "Confirm Exit...", "Are you sure you want to exit ?", qt.QMessageBox.Yes | qt.QMessageBox.No) event.ignore() if result == qt.QMessageBox.Yes: try: with open("last_opened.file", "w") as lo: lo.write(self.file_name) print("name stored") except: pass print("close event") event.accept() #sys.exit() def refresh_fun(self): try: self.browser.load(QUrl(self.file_name)) except: pass def set_type(self): try: ext = self.file_name.split(".")[-1] print(ext) except: ext = "" self.text_edit_field.setLexer(Qsci.QsciLexerCPP(self)) pass if ext == "py": self.text_edit_field.setLexer(Qsci.QsciLexerPython(self)) self.hide_web() print("type python") elif ext == "cpp" or ext == "c": self.text_edit_field.setLexer(Qsci.QsciLexerCPP(self)) self.hide_web() print("type cpp") elif ext == "html": self.text_edit_field.setLexer(Qsci.QsciLexerHTML(self)) self.terminal.setHidden(True) self.browser = QWebView(self) self.browser.load(QUrl(self.file_name)) self.browser.move(self.x_y[0] * 66, 30) self.browser.setFixedSize(self.x_y[0] * 0.33, self.x_y[1] - 20) self.browser.show() #self.browser.move(500,0) #self.setGeometry((self.screen.width()-self.wd+200)/2,0,self.wd,self.ht-50) #self.setFixedSize(self.wd+200,self.ht-50) print("type html") def hide_web(self): #try: print("hide html") self.terminal.setHidden(False) try: self.browser.close() self.browser.setHidden(True) except: pass