class TestWindow(QWidget): TMPDIR = "tmp" TMPHTMLFILE = os.path.join(TMPDIR,'tmptext.html') def __init__(self,parent = None): QWidget.__init__(self,parent) if os.path.isdir(TestWindow.TMPDIR) == False: os.mkdir(TestWindow.TMPDIR) self.textEditor = TextEditor(self) self.webViewer = QWebView(self) self.setupUI() self.textEditor.textChangeSignal.connect(self.reloadText) def reloadText(self,dataDict): if self.webViewer.isVisible() == False: self.webViewer.show() with codecs.open( ResourceManager.getResourceAbsPath('template.html'),'r','utf-8' ) as templateFileObj: templateStr = templateFileObj.read() with open( TestWindow.TMPHTMLFILE,'wb' ) as tempFileObj: tempFileObj.write( (templateStr % dataDict[TextEditor.KEY_ORIGINALTXET]).encode('utf-8') ) self.webViewer.load(QUrl( "file:///%s" % os.path.abspath( TestWindow.TMPHTMLFILE ) )) def setupUI(self): self.webViewer.hide() layout = QHBoxLayout(self) self.splitter = QSplitter(self) self.splitter.addWidget(self.textEditor) self.splitter.addWidget(self.webViewer) layout.addWidget(self.splitter) self.textEditor.resize(400,600) self.webViewer.resize(400,600) self.resize(800,600) self.splitter.setStyleSheet("background-color:green;") self.textEditor.setStyleSheet("background-color:white;border:none;") self.webViewer.setStyleSheet("background-color:white;")
class OAuthWindow(QObject): loginSuccess = pyqtSignal() def __init__(self, VKAPI): super(OAuthWindow, self).__init__() self.__web_view = QWebView() self.__web_view.setFixedSize(400, 400) self.__web_view.setWindowIcon(QIcon('pics/TitleIcon.png')) self.__web_view.setWindowTitle("Authorization") self.__web_view.show() self.__web_view.setUrl( QUrl( 'https://oauth.vk.com/authorize?client_id=' + VKAPI.app_id + '&display=mobile&redirect_uri=http:' + '//vk.com&scope=offline, messages, groups&response_type=code&v=5.60' )) self.__VKAPI = VKAPI self.__web_view.loadFinished.connect(self.loaded) def loaded(self): if '#code=' in self.__web_view.url().toString(): code = self.__web_view.url().toString( )[self.__web_view.url().toString().index("code=") + 5:] self.__web_view.setUrl( QUrl( 'https://oauth.vk.com/access_token?client_id=' + self.__VKAPI.app_id + '&client_secret=aW4JW9GEqR997m3O0rDW&redirect_uri=http://vk.com&code=' + code)) elif 'access_token' in self.__web_view.page().mainFrame().toPlainText( ): access_token = (json.loads( self.__web_view.page().mainFrame().toPlainText().replace( "'", "\""))["access_token"]) self.__web_view.hide() self.__VKAPI.login(access_token) with open('acc.pickle', 'w+b') as file: pickle.dump(access_token, file) self.loginSuccess.emit()
class NodeEditor(QGroupBox): def __init__(self, parent=None): super(NodeEditor, self).__init__(parent) self.setTitle("editor") self.main_layout = QVBoxLayout(self) self.node = None self.storage = get_storage() self.__make_gui() self.storage.eon("node_selected", self.__on_node_selected) self.storage.eon("node_updated", self.__on_node_updated) def __make_gui(self): self.text_edit = QTextEdit() self.main_layout.addWidget(self.text_edit) #--- подсветка синтаксиса # self.hh = PythonHighlighter(self.text_edit.document()) # self.hh = MarkdownHighlighter(self.text_edit) self.web = QWebView() self.main_layout.addWidget(self.web) # self.web.setHtml(html) #--- controls controls = QHBoxLayout() self.main_layout.addLayout(controls) self.btn_view_edit = QPushButton("edit") self.btn_view_edit.clicked.connect(self.__on_set_view_edit) self.btn_view_web = QPushButton("web") self.btn_view_web.clicked.connect(self.__on_set_view_web) self.btn_show_edit = QPushButton("show_edit") self.btn_show_edit.clicked.connect(self.__on_show_edit) self.btn_save = QPushButton("save") self.btn_save.clicked.connect(self.__save) controls.addWidget(self.btn_view_web) controls.addWidget(self.btn_view_edit) controls.addStretch() controls.addWidget(self.btn_show_edit) controls.addStretch() controls.addWidget(self.btn_save) def __on_node_selected(self): # self.node = node self.node = self.storage.get_current_node() self.setTitle(self.node.name) text = self.node.page.raw_text if self.node.meta.ntype == "text": self.__set_view_text(text) elif self.node.meta.ntype == "markdown": self.__set_view_markdown(text) else: pass #--- page text # self.web.setHtml(text) def __on_node_updated(self): self.setTitle(self.node.name) text = self.node.page.raw_text html = markdown.markdown(text) self.web.setHtml(html) def __save(self): #--- get data text = self.text_edit.toPlainText() #--- update node data self.node.update_page_text(text) #--- send events # events.update_current_node() # update_tree - вызовет и это # events.update_tree() def __set_view_text(self, text): self.__on_set_view_edit() self.text_edit.setText(text) self.btn_view_web.setDisabled(True) def __set_view_markdown(self, text): self.__on_set_view_web() self.text_edit.setText(text) html = markdown.markdown(text) self.web.setHtml(html) def __on_set_view_edit(self): self.web.hide() self.text_edit.show() self.btn_view_edit.setDisabled(True) self.btn_view_web.setEnabled(True) self.btn_save.setEnabled(True) def __on_set_view_web(self): self.web.show() self.text_edit.hide() self.btn_view_web.setDisabled(True) self.btn_view_edit.setEnabled(True) self.btn_save.setEnabled(False) def __on_show_edit(self): modal = NodeEditorModal(self.node, self) modal.show()
class Application(QApplication): _active_task = None _exit_timer = None _expects = None _expects_active = False _expects_if_timeout = None _expects_timer = None _frame_data = None _frame_data_lock = None _frame_timer = None _handlers = None _queue = None _trigger_delay_timer = None _visible = True log_event = pyqtSignal(int, str, str) name = '' def __init__(self, name, settings): super(Application, self).__init__([]) self.name = name self.settings = settings self.web_view = QWebView() self._exit_timer = QTimer(self) self._exit_timer.setSingleShot(True) self._exit_timer.setInterval(1000) self._expects = [] self._expects_if_timeout = [] self._expects_timer = QTimer(self) self._expects_timer.setSingleShot(True) self._expects_timer.timeout.connect(self._on_expects_timeout) self._frame_data = {} self._frame_data_lock = Lock() self._frame_timer = QTimer(self) self._frame_timer.start(3000) self._queue = Queue() self._trigger_delay_timer = QTimer(self) self._trigger_delay_timer.setSingleShot(True) self._visible = int(self.settings['application.visible']) self.web_page = WebPage(self.web_view) self.web_page.log_event.connect(self.log_event) self.web_page.frameCreated.connect(self._on_frame_created) self._on_frame_created(self.web_page.mainFrame()) #self.web_page.networkAccessManager().finished.connect( # self._on_http_response) self.web_view.setPage(self.web_page) st = self.web_page.settings() st.setAttribute(st.AutoLoadImages, int(self.settings['application.settings.load_images'])) st.setAttribute( st.JavaEnabled, int(self.settings['application.settings.java_enabled'])) st.setAttribute( st.PluginsEnabled, int(self.settings['application.settings.plugins_enabled'])) self.clear_handlers() # redirect qt related messages try: qInstallMessageHandler(self._pyqt5_null_message_handler) except NameError: qInstallMsgHandler(self._pyqt4_null_message_handler) def start(self): self.process_next_queue() if self._visible: self.web_view.show() return self.exec_() def add_queue(self, task, publish=False): self._queue.put(task) def process_next_queue(self): self._on_next_queue_trigger(self) def get_frame_related_data(self, frame): """ There is a pool of reusable frame data, because browser's frames were easily created and destroyed. """ with self._frame_data_lock: frame_data = self._frame_data.get(str(frame.objectName())) return frame_data def set_expects(self, expects): self._expects_active = True newlist = make_list(expects) for item in newlist: for key in item: if not key in ('path', 'hash', 'host', 'selector_exists', 'selector_not_exists', 'trigger', 'trigger_args', 'trigger_delay', 'trigger_wait_pageload', 'custom'): self.warn('"%s" is not a valid expect field.' % key) item['selector_exists'] = make_list(item.get('selector_exists')) item['selector_not_exists'] = make_list( item.get('selector_not_exists')) self._expects = newlist def set_timeout_expects(self, timeout, expects): self._expects_timer.start(timeout * 1000) self._expects_if_timeout = make_list(expects) def set_upload_files(self, filenames): self.web_page.upload_files = make_list(filenames) def add_handler(self, name, value): self._handlers[name] = value def clear_handlers(self): # clear handlers registration self._handlers = { 'core.next_queue': self._on_next_queue_trigger, 'core.page_not_found': self._on_page_not_found_trigger } def load(self, url): self.web_view.load(QUrl(url)) def info(self, message): self.log_event.emit(INFO, message, 'default') def debug(self, message): self.log_event.emit(DEBUG, message, 'default') def error(self, message): self.log_event.emit(ERROR, message, 'default') def warn(self, message): self.log_event.emit(WARNING, message, 'default') def exit(self, return_code): self.web_view.hide() self._expects_active = False self.web_view.stop() self._exit_timer.timeout.connect( partial(super(Application, self).exit, return_code)) self._exit_timer.start(1000) def _url_matched_expectation(self, expect, scheme, netloc, path, query, segment): if 'host' in expect and not re.match(expect['host'], netloc): self.debug('%s location.host: "%s" "%s"' % (expect['trigger'], expect['host'], netloc)) return False if 'path' in expect and not re.match(expect['path'], path): self.debug('%s location.pathname: "%s" "%s"' % (expect['trigger'], expect['path'], path)) return False if 'hash' in expect and not re.match(expect['hash'], segment): self.debug('%s location.hash: "%s" "%s"' % (expect['trigger'], expect['hash'], segment)) return False return True def process_expectations(self, expect, frame, urlparts): if not self._url_matched_expectation(expect, *urlparts): return document = frame.documentElement() for selector in expect.get('selector_exists', []): if document.findFirst(selector).isNull(): self.debug('%s selector_exists: %s' % (expect['trigger'], selector)) return for selector in expect.get('selector_not_exists', []): if not document.findFirst(selector).isNull(): self.debug('%s selector_not_exists: %s' % (expect['trigger'], selector)) return if 'custom' in expect and not expect['custom'](self, frame, *urlparts): return self.debug('%s triggered.' % expect['trigger']) self._expects_active = False trigger_delay = expect.get('trigger_delay', 0) if trigger_delay: try: self._trigger_delay_timer.timeout.disconnect() except Exception as e: pass self._trigger_delay_timer.timeout.connect( partial(self.trigger, frame=frame, trigger_name=expect['trigger'], trigger_args=expect.get('trigger_args', []))) self._trigger_delay_timer.start(trigger_delay * 1000) else: self.trigger(frame, expect['trigger'], expect.get('trigger_args', [])) def trigger(self, frame, trigger_name, trigger_args): if self._trigger_delay_timer.isActive(): self._trigger_delay_timer.stop() trigger_args = dict((str(key), trigger_args[key]) for \ key in trigger_args) if trigger_name in self._handlers: self._handlers[trigger_name](self, frame, **trigger_args) else: self.error('No handler for trigger %s.' % trigger_name) self.exit(-1) @staticmethod def _on_page_not_found_trigger(app, frame): app.error('PageNotFound: %s.' % frame.baseUrl().toString()) app.exit(-1) @staticmethod def _on_next_queue_trigger(app, frame=None): if not app._active_task is None: app._queue.task_done() try: app._active_task = task = app._queue.get(timeout=15) except Empty: app.info('No more task in the queue.') app.web_view.close() app.exit(0) return expects = make_list(task['expects']) for expect in expects: expect['trigger_wait_pageload'] = True app.set_expects(expects) app.load(task['goto']) def _on_expects_timeout(self): self.debug('No expectations were fulfilled after a periode.') self.set_expects(self._expects_if_timeout or []) self.web_page.triggerAction(QWebPage.Stop) def _on_frame_created(self, frame): """ Called when QWebPage created a QWebFrame """ frame_name = 'frame-' + uuid4().hex frame.setObjectName(frame_name) frame_data = { ENUM_FRAME_DATA_ACTIVE: False, ENUM_FRAME_DATA_TIMER_CALLBACK: partial(self._on_frame_timer, frame=frame), ENUM_FRAME_DATA_TIMER_COUNTER: 0, } with self._frame_data_lock: self._frame_data[frame_name] = frame_data self._frame_timer.timeout.connect( frame_data[ENUM_FRAME_DATA_TIMER_CALLBACK]) frame.destroyed.connect(self._on_frame_destroyed) #frame.javaScriptWindowObjectCleared.connect(partial( # self._on_frame_reset, frame=frame)) frame.loadFinished.connect(partial(self._on_frame_loaded, frame=frame)) def _on_frame_destroyed(self, frame): """ Called when QWebFrame was destroyed """ frame_name = str(frame.objectName()) with self._frame_data_lock: frame_data = self._frame_data.get(frame_name) if frame_data is None: return del self._frame_data[frame_name] self._frame_timer.timeout.disconnect( frame_data[ENUM_FRAME_DATA_TIMER_CALLBACK]) def _on_frame_loaded(self, success, frame=None): self._on_frame_reset(frame) def _on_frame_reset(self, frame=None): """ Called when the javascript `window` object were destroyed, usually just before page reload. """ if frame is None: # this happens if the frame was forced stop, probably, not sure self.error('Problem while loading the page.') self.exit(-1) return self.debug('DOMContentLoaded ' + frame.baseUrl().toString()) frame.evaluateJavaScript(""" window.bot = { click: function(el) { if (el.click) { el.click() } else if (el.fireEvent) { el.fireEvent('onclick'); } else { var evt = document.createEvent('Events'); evt.initEvent('click', true, false); el.dispatchEvent(evt); } }, }; """) frame_data = self.get_frame_related_data(frame) frame_data[ENUM_FRAME_DATA_TIMER_COUNTER] = 0 frame_data[ENUM_FRAME_DATA_ACTIVE] = True def _on_frame_timer(self, frame): if not self._expects_active: # we have obsolete expects return frame_data = self.get_frame_related_data(frame) if frame_data is None: return if not frame_data[ENUM_FRAME_DATA_ACTIVE]: # the frame hasn't been fully loaded return wait_pageload = False urlparts = urlsplit(str(frame.baseUrl().toString())) for expect in self._expects: # is it an obsolete frame if expect.get('trigger_wait_pageload', False) and \ frame_data[ENUM_FRAME_DATA_TIMER_COUNTER] > 0: wait_pageload = True continue self.process_expectations(expect, frame, urlparts) if not self._expects_active: # one of the triggers has been activated break if not wait_pageload: frame_data[ENUM_FRAME_DATA_TIMER_COUNTER] += 1 def _pyqt4_null_message_handler(self, msgtype, msg): """ Nuke Qt related error messages """ self.log_event.emit(DEBUG, str(msg), 'qt') def _pyqt5_null_message_handler(self, msgtype, msgctx, msg): """ Nuke Qt related error messages """ self.log_event.emit(DEBUG, str(msg), 'qt') def _on_http_response(self, response): error = response.error() if error == QNetworkReply.NoError: return url = str(response.url().toString()) scheme, netloc, path, query, segment = urlsplit(url) filename, ext = os.path.splitext(path) if len(ext) and ext[1:] in ('gif', 'css', 'js', 'png', 'jpg', 'jpeg', 'ico'): return status_code = int( response.attribute( QNetworkRequest.HttpStatusCodeAttribute).toInt()) for expect in self._expects: if self._url_matched_expectation(expect, scheme, netloc, path, query, segment): self.log_event.emit(WARNING, '%s: %s' % (status_code, url), 'http') break else: self.log_event.emit(DEBUG, '%s: %s' % (status_code, url), 'http')