コード例 #1
0
ファイル: app_main.py プロジェクト: bcskda/vk-dl
class MainAppliction(QApplication):
    def __init__(self, *argv):
        print('Raw cmdline:', argv)
        super(MainAppliction, self).__init__(*argv)

        self.args = {'source': argv[0][1]}
        params = map(lambda pair: tuple(pair.split(':')), argv[0][2:])
        params = list(params)
        print('Params:', params)
        self.args['params'] = dict(params)
        print('Command-line:', self.args)

        self.web_view = QWebEngineView()
        self.authorizer = app_auth.Authorizer(self.web_view)
        self.extractor = app_extractor.Extractor(self.web_view)
        self.destination = app_destinations.destinations['local']()

        self.authorizer.auth_success.connect(self.on_auth_result)
        self.authorizer.auth_fail.connect(self.on_auth_result)
        # self.authorizer.auth_in_view.connect("""!!!<some button on_click here>!!!""")

        self.extractor.on_data.connect(self.on_extract_data)
        self.extractor.on_finish.connect(self.on_extract_finish)

        self.destination.auth({'dest_file': app_config.local_filename_templ})

        self.attachments = []

    @Core.pyqtSlot(dict, name='on_auth_result')
    def on_auth_result(self, result: dict):
        print(f'Received auth result: {result}')
        self.extractor.set_source(self.args['source'])
        self.extractor.set_parser_params(**self.args['params'])
        self.extractor.execute_in_view(self.web_view)

    @Core.pyqtSlot(list, name='on_extract_data')
    def on_extract_data(self, attachments: list):
        print('MainApplication: received more {}'.format(len(attachments)))
        self.attachments.extend(attachments)
        # ls = map(json.dumps, ls)
        # self.text_view.show()
        # doc = Gui.QTextDocument(self.text_view)
        # doc.setPlainText('\n'.join(ls))
        # self.text_view.setDocument(doc)

    @Core.pyqtSlot(name='on_extract_finish')
    def on_extract_finish(self):
        print('MainApplication: attachment list transmitted')
        self.web_view.close()

        with open('dest/im_photos.json', 'w') as out:
            print(json.dumps(self.attachments), file=out)
        for counter, attach in enumerate(self.attachments):
            print('Saving {} / {}'.format(counter + 1, len(self.attachments)))
            self.destination.upload(attach)

        print('MainApplication: all files saved')
        self.quit()
コード例 #2
0
class WebScraper(QApplication):
    def __init__(self, scraper=DummyScraper()):
        super().__init__([])
        self._scraper = scraper
        self.view = QWebEngineView()
        self.view.loadFinished.connect(self._on_load_finished)

    def _get_headers(self):
        """Return the request headers dictionary from headers.json"""
        headers = {}
        try:
            with open('headers.json') as h:
                headers = json.load(h)
        except:
            pass
        finally:
            return headers

    def _callback(self, page_src):
        """Call the scrape function on page source and close the view"""
        self._scraper.scrape(page_src)
        self.view.close()

    def _on_load_finished(self, successfully):
        """Retrieve the page source a pass it to the default callback"""
        if successfully:
            self.view.page().toHtml(self._callback)
        else:
            print('Fail to load:', self.view.url().toString())
            self.view.close()

    def load(self, url):
        """Load the url and sets the request headers"""
        request = QWebEngineHttpRequest(QUrl(url))
        headers = self._get_headers()

        for h, v in headers.items():
            request.setHeader(bytearray(str(h), 'utf-8'),
                              bytearray(str(v), 'utf-8'))

        self.view.load(request)
コード例 #3
0
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 = QWebEngineView(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.page().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 (RuntimeError) as err:
            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() == QWebEngineContextMenuData.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
コード例 #4
0
ファイル: Func.py プロジェクト: Yee172/AlphabetQs
class MainWin(QtWidgets.QWidget, Ui_Dialog):
    ALPHABET = ALPHABET
    URL = ''
    MODE = 'SEARCH'
    ASKING = 'WORD'
    WORD = None
    CODE = 0
    MORE = 0
    LENGTH = 60
    CONTAIN = 0
    COMMANDING = 1
    WORDLIST_NUM = 0
    HISTORY_NUM = 0
    SEARCH_CONTENT = ''
    RELOAD_CONTENT = '%d-%d' % (ALPHABET.words[0].num, ALPHABET.words[-1].num)
    EMPTY_MODEL = QtGui.QStandardItemModel()

    def __init__(self):
        super(MainWin, self).__init__()
        self.setupUi(self)
        if IS_WINDOWS:
            x, y, width, height = self.button_show_wordlist.geometry().getRect(
            )
            self.button_show_wordlist.setGeometry(
                QtCore.QRect(x + 5, y + 4, width - 10, height - 11))
            x, y, width, height = self.button_quit.geometry().getRect()
            self.button_quit.setGeometry(
                QtCore.QRect(x + 6, y + 4, width - 10, height - 11))
            x, y, width, height = self.button_help.geometry().getRect()
            self.button_help.setGeometry(
                QtCore.QRect(x + 6, y + 4, width - 10, height - 11))
            x, y, width, height = self.button_clear.geometry().getRect()
            self.button_clear.setGeometry(
                QtCore.QRect(x + 8, y + 4, width - 14, height - 11))
            x, y, width, height = self.combo_box_asking.geometry().getRect()
            self.combo_box_asking.setGeometry(
                QtCore.QRect(x + 3, y + 4, width - 6, height - 9))
            x, y, width, height = self.combo_box_mode.geometry().getRect()
            self.combo_box_mode.setGeometry(
                QtCore.QRect(x + 3, y + 4, width - 6, height - 9))
            x, y, width, height = self.button_reload.geometry().getRect()
            self.button_reload.setGeometry(
                QtCore.QRect(x + 8, y + 4, width - 10, height - 9))
        self.initializing()
        self.wordlist_show()

        self.web_viewer = QWebEngineView()
        self.web_viewer.setObjectName('web_viewer')
        # self.channel = QWebChannel(self.web_viewer)
        # self.web_viewer.setGeometry(QtCore.QRect(800, 20, 360, 450))

        self.button_show_wordlist.clicked.connect(self.wordlist_click)
        self.button_help.clicked.connect(self.show_help)
        self.button_clear.clicked.connect(self.clear)
        self.console.returnPressed.connect(self.console_operate)
        self.check_code.stateChanged.connect(self.code_update)
        self.check_more.stateChanged.connect(self.more_info)
        self.combo_box_asking.currentIndexChanged.connect(self.asking_update)
        self.combo_box_mode.currentIndexChanged.connect(self.mode_update)
        self.search_box.textChanged.connect(self.search)
        self.button_reload.clicked.connect(self.wordlist_reload)
        _translate = QCoreApplication.translate
        self.reload_selection_box.setPlaceholderText(
            _translate("Dialog", self.RELOAD_CONTENT))
        self.show()

    def code_update(self):
        self.CODE = self.check_code.checkState()

    def mode_update(self):
        self.MODE = self.combo_box_mode.currentText()
        self.console_show_history.append('MODE changed into %s' % self.MODE)

    def asking_update(self):
        self.ASKING = self.combo_box_asking.currentText()
        self.console_show_history.append('ASKING changed into %s' %
                                         self.ASKING)

    def more_info(self):
        self.MORE = self.check_more.checkState()
        if self.MORE:
            # self.resize(1200, 562)
            if self.WORD is not None:
                self.URL = 'http://www.youdao.com/w/eng/%s' % self.WORD.word.lower(
                )
            if self.URL:
                self.web_viewer.setUrl(QUrl(self.URL))
                self.web_viewer.show()
        else:
            # self.resize(770, 562)
            self.web_viewer.close()
            pass

    def initializing(self):
        self.console_show_history.append('Initial mode is SEARCH MODE!')
        self.console_show_history.append('You can input word or num!')
        self.console_show_history.append(
            'Input `help` for more commands available!')

    def search_words(self, content):
        result = []
        try:
            temp = int(content)
            if self.CONTAIN:
                for each in self.ALPHABET.words:
                    if content in '%03d' % each.num:
                        result.append(each)
                return result
            else:
                for each in self.ALPHABET.words:
                    if content == ('%03d' % each.num)[:len(content)]:
                        result.append(each)
                return result
        except:
            content = content.lower()
            if self.CONTAIN:
                for each in self.ALPHABET.words:
                    if content in each.word.lower():
                        result.append(each)
                return result
            else:
                for each in self.ALPHABET.words:
                    if content == each.word[:len(content)].lower():
                        result.append(each)
                return result

    def search(self):
        self.SEARCH_CONTENT = self.search_box.text()
        if self.SEARCH_CONTENT:
            if self.button_show_wordlist.text() == 'HIDE':
                self.wordlist_show(self.search_words(self.SEARCH_CONTENT))
        else:
            if self.button_show_wordlist.text() == 'HIDE':
                self.wordlist_show()

    def wordlist_reload(self):
        _translate = QCoreApplication.translate
        self.RELOAD_CONTENT = self.reload_selection_box.text()
        try:
            left, right = map(int, self.RELOAD_CONTENT.split('-'))
            left, right = min(left, right), max(left, right)
            READ_IN = read_in(PATH)
            self.ALPHABET = Alphabet([
                Word(READ_IN.loc[i]) for i in range(len(READ_IN))
                if left <= int(READ_IN.loc[i]['#']) <= right
            ])
            del READ_IN
            self.wordlist_show()
        except:
            self.reload_selection_box.setText(_translate("Dialog", ''))

    def wordlist_click(self):
        content = self.button_show_wordlist.text()
        if content == 'SHOW':
            if self.SEARCH_CONTENT:
                self.wordlist_show(self.search_words(self.SEARCH_CONTENT))
            else:
                self.wordlist_show()
        if content == 'HIDE':
            self.wordlist_hide()

    def wordlist_show(self, words=None):
        if words is None:
            words = self.ALPHABET.words
        wordlist_model = QtGui.QStandardItemModel(0, 2, self)
        wordlist_model.setHeaderData(0, Qt.Horizontal, '#')
        wordlist_model.setHeaderData(1, Qt.Horizontal, 'WORD')
        self.wordlist.setModel(wordlist_model)
        self.wordlist.setColumnWidth(0, 40)
        self.wordlist.setColumnWidth(1, 30)
        self.WORDLIST_NUM = 0
        if words:
            for each in words:
                self.add_data(wordlist_model, '%03d' % each.num, each.word)
        _translate = QCoreApplication.translate
        self.button_show_wordlist.setText(_translate("Dialog", "HIDE"))

    def wordlist_hide(self):
        self.wordlist.setModel(self.EMPTY_MODEL)
        _translate = QCoreApplication.translate
        self.button_show_wordlist.setText(_translate("Dialog", "SHOW"))

    def add_data(self, model, num, word):
        model.insertRow(self.WORDLIST_NUM)
        model.setData(model.index(self.WORDLIST_NUM, 0), num)
        model.setData(model.index(self.WORDLIST_NUM, 1), word)
        self.WORDLIST_NUM += 1

    def show_help(self):
        self.console_show_history.append(''.center(self.LENGTH, '-'))
        self.console_show_history.append('help'.ljust(7) + '\t->\t' +
                                         'Help information')
        self.console_show_history.append('clear'.ljust(7) + '\t->\t' +
                                         'Clear all the history')
        self.console_show_history.append('mode'.ljust(7) + '\t->\t' +
                                         'Show or change the mode')
        self.console_show_history.append(''.ljust(7) + '\t\t      Example: ' +
                                         'mode random')
        self.console_show_history.append('show'.ljust(7) + '\t->\t' +
                                         'Show the word list')
        self.console_show_history.append('hide'.ljust(7) + '\t->\t' +
                                         'Hide the word list')
        self.console_show_history.append('code'.ljust(7) + '\t->\t' +
                                         'Show more info')
        self.console_show_history.append('quit'.ljust(7) + '\t->\t' +
                                         'Exit the program')
        self.console_show_history.append(''.center(self.LENGTH, '-'))

    def clear(self):
        self.HISTORY_NUM = 0
        self.WORD = None
        self.console_show_history.clear()
        self.info_clear()
        self.label_info.setText('')

    def console_operate(self):
        def read(content):
            content = content.lower()
            if self.WORD is None:
                if content == 'help':
                    self.show_help()
                elif content == 'clear':
                    self.clear()
                elif content[:4] == 'mode':
                    mode = str_process(content[4:])
                    if not mode:
                        mode_change()
                    elif mode in ['s', 'sear', 'search']:
                        mode_change('SEARCH')
                    elif mode in ['r', 'rand', 'random']:
                        mode_change('RANDOM')
                    elif mode in ['o', 'ord', 'order']:
                        mode_change('ORDER')
                elif content == 'show':
                    self.wordlist_show()
                    self.console_show_history.append('Word list showed')
                elif content == 'hide':
                    self.wordlist_hide()
                    self.console_show_history.append('word list hid')
                elif content == 'quit':
                    self.close()
                elif content[:6] == 'asking':
                    asking = str_process(content[6:])
                    if not asking:
                        asking_change()
                    elif asking == 'word':
                        asking_change('WORD')
                    elif asking in ['def', 'definition']:
                        asking_change('DEFINITION')
                    elif asking in ['sam', 'sample']:
                        asking_change('SAMPLE')
                    elif asking in ['thes', 'thesaurus']:
                        asking_change('THESAURUS')
                elif self.MODE == 'SEARCH':
                    try:
                        content = int(content)
                    except:
                        pass
                    self.WORD = find_word(content)
                    if self.WORD is None:
                        self.console_show_history.append('Undefined')
                    else:
                        ask()
                elif self.MODE == 'RANDOM':  # TODO
                    self.WORD = self.ALPHABET.get_random()
                    ask()
                elif self.MODE == 'ORDER':
                    self.WORD = self.ALPHABET.get_next()
                    ask()
                else:
                    self.console_show_history.append('Undefined')
            else:
                if self.ASKING in ['WORD', 'SAMPLE', 'THESAURUS']:
                    search_w(self.WORD, content)
                if self.ASKING == 'DEFINITION':
                    search_q(self.WORD, content)
                self.WORD = None
                self.label_info.setText('')

        def ask():
            self.info_clear()
            if self.ASKING == 'WORD':
                self.label_def_show.setText(self.WORD.html_definition())
                self.label_info.setText('[Word required]')
            if self.ASKING == 'DEFINITION':
                self.label_word_show.setText(self.WORD.word)
                self.label_info.setText('Definition of [%s] required' %
                                        self.WORD.word)
            if self.ASKING == 'SAMPLE':
                self.label_samp_show.setText(self.WORD.html_sample_hollow())
                self.label_info.setText('[Word required]')
            if self.ASKING == 'THESAURUS':
                thesaurus = self.WORD.get_random_html_thesaurus()
                self.label_thesaurus_show.setText(thesaurus)
                if thesaurus == 'thesaurus NOT available':
                    self.label_info.setText('[Word required]')
                else:
                    self.label_info.setText(
                        '[Word has the same meaning of [%s] required]' %
                        thesaurus)

        def asking_change(asking=''):
            if asking:
                self.ASKING = asking
                self.combo_box_asking.setCurrentIndex({
                    'WORD': 0,
                    'DEFINITION': 1,
                    'SAMPLE': 2,
                    'THESAURUS': 3
                }[asking])
            else:
                self.console_show_history.append('ASKING now is %s' %
                                                 self.ASKING)

        def mode_change(mode=''):
            if mode:
                self.MODE = mode
                self.combo_box_mode.setCurrentIndex({
                    'SEARCH': 0,
                    'RANDOM': 1,
                    'ORDER': 2
                }[mode])
            else:
                self.console_show_history.append('MODE now is %s' % self.MODE)

        def search_q(word, definition):
            self.console_show_history.append(''.center(self.LENGTH, '-'))
            if str_process(word.definition) == str_process(definition):
                self.console_show_history.append(
                    '<html>'
                    '<span style=" font-weight:600; color:#ff0000;">'
                    '√'
                    '</span></html>')
            else:
                self.console_show_history.append(
                    '<html>'
                    '<span style=" font-weight:600; color:#ff0000;">'
                    '×'
                    '</span></html>')
            self.console_show_history.append(
                '↓        Your definition        ↓\n%s' % definition)
            self.console_show_history.append(
                '%s\n↑        Real definition        ↑' % word.definition)
            self.console_show_history.append(''.center(self.LENGTH, '-'))
            self.info_show(word)

        def search_w(word, w):
            self.console_show_history.append(''.center(self.LENGTH, '-'))
            if str_process(word.word) == str_process(w):
                self.console_show_history.append(
                    '<html>'
                    '<span style=" font-weight:600; color:#ff0000;">'
                    '√'
                    '</span></html>')
            else:
                self.console_show_history.append(
                    '<html>'
                    '<span style=" font-weight:600; color:#ff0000;">'
                    '×'
                    '</span></html>')
            self.console_show_history.append(
                '↓        Your word        ↓\n%s' % w)
            self.console_show_history.append(
                '%s\n↑        Real word        ↑' % word.word)
            self.console_show_history.append(''.center(self.LENGTH, '-'))
            self.info_show(word)

        self.HISTORY_NUM += 1
        text = self.console.text()
        self.console.setText('')
        if self.CODE:
            self.console_show_history.append('[%d]>>> ' % self.HISTORY_NUM +
                                             text)
        read(text)

    def keyPressEvent(self, QKeyEvent):
        if QKeyEvent.key() == Qt.Key_Escape:
            self.close()

    def info_show(self, word):
        self.label_word_show.setText(word.word)
        self.label_def_show.setText(word.html_definition())
        self.label_samp_show.setText(word.html_sample())
        self.label_thesaurus_show.setText(word.html_thesaurus())

    def info_clear(self):
        self.label_word_show.setText('')
        self.label_def_show.setText('')
        self.label_samp_show.setText('')
        self.label_thesaurus_show.setText('')
コード例 #5
0
class MyWindow_v2(QtWidgets.QMainWindow):
    def __init__(self):
        super(MyWindow_v2, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        cur_dtime = datetime.now()
        self.fname = None
        self.current_date = str(cur_dtime.date())
        list_theory = [
            self.ui.theoryCombobox.itemText(i)
            for i in range(self.ui.theoryCombobox.count())
        ]
        self.liquids = {item: [] for item in list_theory}
        self.current_index = 0
        self.image_index = None
        self.__setupUI()
        self.result = []

    @pyqtSlot()
    def __setupUI(self):
        # add WebView widget
        self.web = QWebView()
        self.web.setUrl(QUrl(THEORY))
        self.ui.gridLayout_5.addWidget(self.web)

        # add setting widget
        self.Form = QtWidgets.QWidget()
        self.settings_ui = setting_ui()
        self.settings_ui.setupUi(self.Form)
        self.ui.gridLayout_5.addWidget(self.Form)

        self.Form.close()

        # add result widget
        self.Result = QtWidgets.QWidget()
        self.result_ui = result_ui()
        self.result_ui.setupUi(self.Result)
        self.ui.resultGridLayout.addWidget(self.Result)
        self.Result.close()

        # action menu clicked
        self.ui.actionSave_Result.triggered.connect(self.save_result)
        self.ui.actionOpenProject.triggered.connect(self.load_result)
        self.ui.actionNew.triggered.connect(self.new)
        self.ui.actionExit.triggered.connect(self.exit)

        self.ui.theoryButton.setEnabled(False)
        self.ui.theoryCombobox.setEnabled(False)
        self.ui.tempCombobox.setCurrentIndex(2)
        self.ui.theoryButton.clicked.connect(self.__theory_button_checked)
        self.ui.settingsButton.clicked.connect(self.__settings_button_checked)
        self.ui.measurmentButton.clicked.connect(
            self.__measurment_buttton_checked)
        self.ui.theoryCombobox.currentTextChanged.connect(self.__theory_chose)
        self.ui.tempCombobox.currentTextChanged.connect(self.__fullfill_table)

        self.settings_ui.calculateButton.clicked.connect(
            self.__collect_data_to_process)
        self.settings_ui.calculateButton.setEnabled(False)
        self.settings_ui.calculateButton.setStyleSheet(
            "color: rgb(255, 0, 0);")
        self.settings_ui.addliquidButton.clicked.connect(
            self.__add_liquid_button_clicked)

        self.result_ui.resultTable.setColumnCount(4)
        self.result_ui.resultTable.setHorizontalHeaderLabels(
            HORIZONTAL_HEADER_RESULT)
        self.result_ui.resultTable.setRowCount(5)

    @pyqtSlot()
    def __theory_button_checked(self):
        self.Form.close()
        self.Result.close()
        self.web.show()
        self.resize(1246, 564)
        self.ui.theoryCombobox.setEnabled(False)
        self.ui.theoryButton.setEnabled(False)
        self.ui.settingsButton.setEnabled(True)
        self.ui.measurmentButton.setEnabled(True)

    @pyqtSlot()
    def __settings_button_checked(self):
        self.Form.close()
        self.Result.close()
        # TODO Исправить этот костыль (web.show() => web.close())
        self.web.show()
        self.resize(1246, 564)
        self.web.close()
        self.Form.show()
        self.ui.theoryCombobox.setEnabled(True)
        self.ui.theoryButton.setEnabled(True)
        self.ui.settingsButton.setEnabled(False)
        self.ui.measurmentButton.setEnabled(True)

    @pyqtSlot()
    def __measurment_buttton_checked(self):
        self.web.close()
        self.Form.show()
        self.Result.show()
        self.ui.theoryCombobox.setEnabled(True)
        self.ui.theoryButton.setEnabled(True)
        self.ui.settingsButton.setEnabled(True)
        self.ui.measurmentButton.setEnabled(False)

    @pyqtSlot()
    def __add_liquid_button_clicked(self):
        Dialog = QtWidgets.QDialog()
        ui = Ui_Dialog(LIQUIDS[self.ui.theoryCombobox.currentText()])
        ui.setupUi(Dialog)
        Dialog.show()
        rsp = Dialog.exec_()
        if rsp == QtWidgets.QDialog.Accepted:
            self.liquids[self.ui.theoryCombobox.currentText()] = []
            for index in range(ui.myList.count()):
                self.liquids[self.ui.theoryCombobox.currentText()].append(
                    ui.myList.item(index).text())
            self.__create_input_bar()
            self.__fullfill_table()
        else:
            pass

    @pyqtSlot()
    def __create_input_bar(self):
        self.lineedit_list = []
        self.liquids_inuse_list = []
        self.__delete_widgets_on_setting_layout()
        self.settings_ui.gridLayout.setColumnStretch(0, 1)
        self.settings_ui.gridLayout.setColumnStretch(1, 1)
        for index, liquid in enumerate(
                self.liquids[self.ui.theoryCombobox.currentText()]):
            new_label = QtWidgets.QLabel(text=liquid)
            new_label.setFixedHeight(30)
            new_label.setSizePolicy(
                QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                      QtWidgets.QSizePolicy.Expanding))
            new_label.setAlignment(QtCore.Qt.AlignCenter)
            self.liquids_inuse_list.append(new_label)
            new_lineedit = QtWidgets.QLineEdit()
            new_lineedit.textChanged.connect(self.check_all)
            self.lineedit_list.append(new_lineedit)
            new_lineedit.setAlignment(QtCore.Qt.AlignCenter)
            reg_ex = QRegExp("[0-9]+.[0-9]{,3}")
            input_validator = QRegExpValidator(reg_ex, new_lineedit)
            new_lineedit.setValidator(input_validator)
            self.settings_ui.gridLayout_2.addWidget(new_label, index, 0)
            self.settings_ui.gridLayout_2.addWidget(new_lineedit, index, 1)

    @pyqtSlot()
    def __fullfill_table(self):
        self.__clear_table_on_setting_layout()
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                           QtWidgets.QSizePolicy.Maximum)
        self.settings_ui.tableWidget.setSizePolicy(sizePolicy)
        self.settings_ui.tableWidget.setColumnCount(4)
        self.settings_ui.tableWidget.verticalHeader().hide()
        self.settings_ui.tableWidget.setHorizontalHeaderLabels(
            HORIZONTAL_HEADER_SETTING)
        self.settings_ui.tableWidget.setColumnWidth(0, 150)
        self.settings_ui.tableWidget.setColumnWidth(1, 100)
        self.settings_ui.tableWidget.setColumnWidth(2, 100)
        self.settings_ui.tableWidget.setColumnWidth(3, 100)
        if self.liquids[self.ui.theoryCombobox.currentText()]:
            liquids = self.liquids[self.ui.theoryCombobox.currentText()]
            cur_temp = self.ui.tempCombobox.currentText()[:-1]
            self.settings_ui.tableWidget.setRowCount(len(liquids))
            for index, liquid in enumerate(liquids):
                item0 = QTableWidgetItem(liquid)
                item0.setTextAlignment(QtCore.Qt.AlignCenter)
                item1 = QTableWidgetItem(str(TEMPERATURE[liquid][cur_temp][1]))
                item1.setTextAlignment(QtCore.Qt.AlignCenter)
                item2 = QTableWidgetItem(str(TEMPERATURE[liquid][cur_temp][0]))
                item2.setTextAlignment(QtCore.Qt.AlignCenter)
                item3 = QTableWidgetItem(str(
                    TEMPERATURE[liquid][cur_temp][-1]))
                item3.setTextAlignment(QtCore.Qt.AlignCenter)

                self.settings_ui.tableWidget.setItem(index, 0, item0)
                self.settings_ui.tableWidget.setItem(index, 1, item1)
                self.settings_ui.tableWidget.setItem(index, 2, item2)
                self.settings_ui.tableWidget.setItem(index, 3, item3)

    @pyqtSlot()
    def __delete_widgets_on_setting_layout(self):
        for index in range(self.settings_ui.gridLayout_2.count()):
            self.settings_ui.gridLayout_2.itemAt(index).widget().deleteLater()

    @pyqtSlot()
    def __clear_table_on_setting_layout(self):
        self.settings_ui.tableWidget.clear()
        self.settings_ui.tableWidget.setRowCount(0)

    def __clear_table_on_result_layout(self):
        self.result_ui.resultTable.clear()
        self.result_ui.resultTable.setRowCount(0)

    @pyqtSlot()
    def __theory_chose(self):
        self.settings_ui.calculateButton.setStyleSheet(
            "color: rgb(255, 0, 0);")
        try:
            self.liquids[self.ui.theoryCombobox.currentText()]
        except Exception:
            self.liquids[self.ui.theoryCombobox.currentText()] = []
        finally:
            self.__delete_widgets_on_setting_layout()
            self.__clear_table_on_setting_layout()
            self.__create_input_bar()
            self.__fullfill_table()

    def __collect_data_to_process(self):
        self.to_process = []
        cur_temp = self.ui.tempCombobox.currentText()[:-1]
        method = self.ui.theoryCombobox.currentText()

        for index, line in enumerate(self.lineedit_list):
            polar = TEMPERATURE[
                self.liquids_inuse_list[index].text()][cur_temp][1]
            dispersive = TEMPERATURE[
                self.liquids_inuse_list[index].text()][cur_temp][0]
            if method == 'van-Oss':
                acid = TEMPERATURE[
                    self.liquids_inuse_list[index].text()][cur_temp][2]
                base = TEMPERATURE[
                    self.liquids_inuse_list[index].text()][cur_temp][3]
                self.to_process.append(
                    (self.liquids_inuse_list[index].text(), float(line.text()),
                     dispersive, polar, acid, base))
            elif method == 'Owens-Wendt' or method == 'Fowkes':
                self.to_process.append((self.liquids_inuse_list[index].text(),
                                        float(line.text()), dispersive, polar))
            else:
                self.to_process.append(
                    (self.liquids_inuse_list[index].text(), float(line.text()),
                     dispersive + polar))
            self.image_index = 0 if self.image_index is None else self.image_index + 1
        self.__process()

    def __process(self):
        math = Calculation(to_process=self.to_process,
                           name=self.ui.theoryCombobox.currentText(),
                           index=self.image_index)
        try:
            math.calculate()
        except ValueError as v_error:
            self._save_to_log(v_error)
            self.__raise_error()
        else:
            self.result.append(math.result)
            self._add_to_result_table()
            self._add_plot()

    def _save_to_log(self, *args):
        with open('error.log', mode='a+', encoding='utf8') as flog:
            line = f'{datetime.now()}:{[arg for arg in args]}\n'
            flog.write(line)

    @pyqtSlot()
    def __raise_error(self):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)

        msg.setText("Hello! It seems like something going wrong =( "
                    "If you need details look errors.log file in the source ")
        msg.setInformativeText(
            "Result that we try to obtaine is bullsheety, check the data twice!"
        )
        msg.setWindowTitle("Calculation Error")
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        retval = msg.exec_()

    @pyqtSlot()
    def _add_plot(self):
        if self.ui.theoryCombobox.currentText() != 'Fowkes':
            name = self.ui.theoryCombobox.currentText()
            pixmap = QPixmap(
                f'result/{self.current_date}_{name}({self.image_index}).png')
            self.result_ui.plotLabel.setPixmap(pixmap)
        else:
            self.result_ui.plotLabel.setText(
                f'No plot for this "{self.ui.theoryCombobox.currentText()}"method!'
            )

    @pyqtSlot()
    def _add_to_result_table(self):
        align = QtCore.Qt.AlignCenter
        if len(self.result) > self.current_index:
            new_result = self.result[-1]
            self.result_ui.resultTable.setRowCount(len(self.result)) if len(
                self.result) > 4 else self.result_ui.resultTable.setRowCount(5)
            try:
                item0 = QTableWidgetItem(str(round(new_result[0], 3)))
                item1 = QTableWidgetItem(str(round(new_result[1], 3)))
            except Exception:
                item0 = QTableWidgetItem(str(new_result[0]))
                item1 = QTableWidgetItem(str(new_result[1]))
            finally:
                item0.setTextAlignment(align)
                self.result_ui.resultTable.setItem(self.current_index, 0,
                                                   item0)
                item1.setTextAlignment(align)
                self.result_ui.resultTable.setItem(self.current_index, 1,
                                                   item1)
                item2 = QTableWidgetItem(str(round(new_result[2], 3)))
                item2.setTextAlignment(align)
                self.result_ui.resultTable.setItem(self.current_index, 2,
                                                   item2)
                item3 = QTableWidgetItem(str(new_result[3]))
                item3.setTextAlignment(align)
                self.result_ui.resultTable.setItem(self.current_index, 3,
                                                   item3)
                self.current_index += 1

    @pyqtSlot()
    def check_all(self):
        a = [line.text() for line in self.lineedit_list if line.text() != '']
        if len(a) == self.settings_ui.gridLayout_2.count() // 2:
            self.settings_ui.calculateButton.setStyleSheet(
                "color: rgb(250, 120, 255);")
            self.settings_ui.calculateButton.setEnabled(True)
        else:
            self.settings_ui.calculateButton.setStyleSheet(
                "color: rgb(255, 0, 0);")
            self.settings_ui.calculateButton.setEnabled(False)

    @pyqtSlot()
    def save_result(self):
        '''
        Save result of current measurment as .txt file
        '''
        home_dir = os.getcwd()
        open_dir = f'{home_dir}/result'
        if not self.fname:
            self.fname = QFileDialog.getSaveFileName(
                self, 'Open file', open_dir, filter='Text files (*.txt)')
        try:
            with open(self.fname[0], 'a+') as f:
                for measurment in self.result:
                    f.write('\n')
                    for value in measurment:
                        f.write(f'{str(value)},')
        except Exception as exc:
            print(exc)
            pass

    @pyqtSlot()
    def load_result(self):
        '''
        Save result of current measurment as .txt file
        '''
        result_dir = os.getcwd()
        self.fname = QFileDialog.getOpenFileNames(self,
                                                  'Open file',
                                                  result_dir,
                                                  filter='Text files (*.txt)')
        with open(self.fname[0][-1], 'r+') as f:
            result = [value[:-1] for value in f.readlines() if value != '\n']
            for value in result:
                to_read = value.split(sep=',')
                self.result.append(to_read) if len(
                    to_read) == 4 else self.result.append(to_read[:-1])
        self.current_index = 0
        self.__measurment_buttton_checked()
        self.__to_table()

    def __to_table(self):
        self.result_ui.resultTable.setColumnCount(4)
        self.result_ui.resultTable.setHorizontalHeaderLabels(
            HORIZONTAL_HEADER_RESULT)
        self.result_ui.resultTable.setRowCount(5)
        self.result_ui.resultTable.horizontalHeader().setStretchLastSection(
            True)
        align = QtCore.Qt.AlignCenter
        self.result_ui.resultTable.setRowCount(len(self.result))
        for value in self.result:
            item0 = QTableWidgetItem(value[0][:4])
            item1 = QTableWidgetItem(value[1][:4])
            item2 = QTableWidgetItem(value[2][:4])
            item3 = QTableWidgetItem(value[3])

            item0.setTextAlignment(align)
            item1.setTextAlignment(align)
            item2.setTextAlignment(align)
            item3.setTextAlignment(align)

            self.result_ui.resultTable.setItem(self.current_index, 0, item0)
            self.result_ui.resultTable.setItem(self.current_index, 1, item1)
            self.result_ui.resultTable.setItem(self.current_index, 2, item2)
            self.result_ui.resultTable.setItem(self.current_index, 3, item3)
            self.current_index += 1

    @pyqtSlot()
    def new(self):
        self.__delete_widgets_on_setting_layout()
        self.__clear_table_on_setting_layout()
        self.__clear_table_on_result_layout()

        self.result_ui.resultTable.setColumnCount(4)
        self.result_ui.resultTable.setHorizontalHeaderLabels(
            HORIZONTAL_HEADER_RESULT)
        self.result_ui.resultTable.setRowCount(5)

        self.result_ui.plotLabel.setText('Plot will be here')

    @pyqtSlot()
    def exit(self):
        self.save_result()
        self.close()
コード例 #6
0
class Overlay(QtCore.QObject):

    def __init__(self, up, configFileName, name, url):
        super().__init__()
        self.configFileName = configFileName
        self.parent = up
        self.app = up.app
        self.url = url
        self.size = None
        self.name = name
        self.overlay = None
        self.settings = None
        self.position = None
        self.enabled = True
        self.showtitle = True
        self.mutedeaf = True
        self.showtitle = True

    def load(self):
        config = ConfigParser(interpolation=None)
        config.read(self.configFileName)
        self.posXL = config.getint(self.name, 'xl', fallback=0)
        self.posXR = config.getint(self.name, 'xr', fallback=200)
        self.posYT = config.getint(self.name, 'yt', fallback=50)
        self.posYB = config.getint(self.name, 'yb', fallback=450)
        self.right = config.getboolean(self.name, 'rightalign', fallback=False)
        self.mutedeaf = config.getboolean(
            self.name, 'mutedeaf', fallback=True)
        self.chatresize = config.getboolean(
            self.name, 'chatresize', fallback=True)
        self.screenName = config.get(self.name, 'screen', fallback='None')
        #self.url = config.get(self.name, 'url', fallback=None)
        self.enabled = config.getboolean(self.name, 'enabled', fallback=True)
        self.showtitle = config.getboolean(self.name, 'title', fallback=True)
        self.hideinactive = config.getboolean(
            self.name, 'hideinactive', fallback=True)
        self.chooseScreen()
        # TODO Check, is there a better logic location for this?
        if self.enabled:
            self.showOverlay()

    def moveOverlay(self):
        if self.overlay:
            self.overlay.resize(self.posXR-self.posXL, self.posYB-self.posYT)
            self.overlay.move(self.posXL + self.screenOffset.left(),
                              self.posYT + self.screenOffset.top())

    def on_url(self, url):
        if self.overlay:
            self.overlay.load(QtCore.QUrl(url))
        self.url = url
        self.save()
        self.settings.close()
        self.settings = None

    def on_save_position(self, url):
        self.save()
        self.position.close()
        self.position = None

    @pyqtSlot()
    def save(self):
        config = ConfigParser(interpolation=None)
        config.read(self.configFileName)
        if not config.has_section(self.name):
            config.add_section(self.name)
        config.set(self.name, 'xl', '%d' % (self.posXL))
        config.set(self.name, 'xr', '%d' % (self.posXR))
        config.set(self.name, 'yt', '%d' % (self.posYT))
        config.set(self.name, 'yb', '%d' % (self.posYB))
        config.set(self.name, 'rightalign', '%d' % (int(self.right)))
        config.set(self.name, 'mutedeaf', '%d' % (int(self.mutedeaf)))
        config.set(self.name, 'chatresize', '%d' % (int(self.chatresize)))
        config.set(self.name, 'screen', self.screenName)
        config.set(self.name, 'enabled', '%d' % (int(self.enabled)))
        config.set(self.name, 'title', '%d' % (int(self.showtitle)))
        config.set(self.name, 'hideinactive', '%d' % (int(self.hideinactive)))
        if self.url:
            config.set(self.name, 'url', self.url)
            if ',' in self.url:
                self.parent.reinit()
        with open(self.configFileName, 'w') as file:
            config.write(file)

    @pyqtSlot()
    def on_click(self):
        self.runJS(
            "document.getElementsByClassName('source-url')[0].value;", self.on_url)

    @pyqtSlot()
    def skip_stream_button(self):
        skipIntro = "buttons = document.getElementsByTagName('button');for(i=0;i<buttons.length;i++){if(buttons[i].innerHTML=='Install for OBS'){buttons[i].click()}}"
        hideLogo = "document.getElementsByClassName('install-logo')[0].style.setProperty('display','none');"
        resizeContents = "document.getElementsByClassName('content')[0].style.setProperty('top','30px');"
        resizeHeader = "document.getElementsByClassName('header')[0].style.setProperty('height','35px');"
        hidePreview = "document.getElementsByClassName('config-link')[0].style.setProperty('height','300px');document.getElementsByClassName('config-link')[0].style.setProperty('overflow','hidden');"
        hideClose = "document.getElementsByClassName('close')[0].style.setProperty('display','none');"
        chooseVoice = "for( let button of document.getElementsByTagName('button')){ if(button.getAttribute('value') == 'voice'){ button.click(); } }"
        chooseChat = "for( let button of document.getElementsByTagName('button')){ if(button.getAttribute('value') == 'chat'){ button.click(); } }"
        infoListener = "if(typeof console.oldlog === 'undefined'){console.oldlog=console.log;}window.consoleCatchers=[];console.log = function(text,input){if(typeof input !== 'undefined'){window.consoleCatchers.forEach(function(item,index){item(input)})}else{console.oldlog(text);}};"
        catchGuild = "window.consoleCatchers.push(function(input){if(input.cmd == 'GET_GUILD'){window.guilds=input.data.id}})"
        catchChannel = "window.consoleCatchers.push(function(input){if(input.cmd == 'GET_CHANNELS'){window.channels = input.data.channels;}})"

        self.runJS(skipIntro)
        self.runJS(hideLogo)
        self.runJS(resizeContents)
        self.runJS(hidePreview)
        self.runJS(resizeHeader)
        self.runJS(hideClose)
        self.runJS(infoListener)
        self.runJS(catchGuild)
        self.runJS(catchChannel)
        if self.url:
            if 'overlay/voice' in self.url:
                self.runJS(chooseVoice)
            else:
                self.runJS(chooseChat)

    def enableConsoleCatcher(self):
        if self.overlay:
            tweak = "if(typeof console.oldlog === 'undefined'){console.oldlog=console.log;}window.consoleCatchers=[];console.log = function(text,input){if(typeof input !== 'undefined'){window.consoleCatchers.forEach(function(item,index){item(input)})}else{console.oldlog(text);}};"
            self.overlay.page().runJavaScript(tweak)

    def enableShowVoiceTitle(self):
        if self.overlay:
            tweak = "window.consoleCatchers.push(function(input){if(input.cmd == 'GET_CHANNEL'){chan=input.data.name;(function() { css = document.getElementById('title-css'); if (css == null) { css = document.createElement('style'); css.type='text/css'; css.id='title-css'; document.head.appendChild(css); } css.innerText='.voice-container:before{content:\"'+chan+'\";background:rgba(30, 33, 36, 0.95);padding:4px 6px;border-radius: 3px;}';})()}})"
            self.overlay.page().runJavaScript(tweak)

    def enableHideInactive(self):
        if self.overlay and self.url:
            if 'overlay/voice' in self.url:
                tweak = "document.getElementById('app-mount').style='display:none';window.consoleCatchers.push(function(input){if(input.cmd=='AUTHENTICATE'){console.error(input.data.user);window.iAm=input.data.user.username;}if(input.evt=='VOICE_STATE_CREATE' || input.evt=='VOICE_STATE_UPDATE'){if(input.data.nick.toUpperCase()==window.iAm.toUpperCase()){document.getElementById('app-mount').style='display:block';console.error('Showing '+chan)}}if(input.evt=='VOICE_STATE_DELETE'){if(input.data.nick.toUpperCase()==window.iAm.toUpperCase()){document.getElementById('app-mount').style='display:none';console.error('Hiding '+chan)}}});"
                self.overlay.page().runJavaScript(tweak)

    def enableMuteDeaf(self):
        if self.overlay:
            tweak = "window.consoleCatchers.push(function(input){if(input.evt == 'VOICE_STATE_UPDATE'){name=input.data.nick;uState = input.data.voice_state;muteicon = '';if(uState.self_mute || uState.mute){muteicon='<img src=\\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAABhMAAAYJQE8CCw1AAAAB3RJTUUH5AUGCx0VMm5EjgAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAABzSURBVDjLxZIxCsAwCEW/oT1P7z93zZJjeIYMv0sCIaBoodTJDz6/JgJfBslOsns1xYONvK66JCeqAC4ALTz+dJvOo0lu/zS87p2C98IdHlq9Buo5D62h17amScMk78hBWXB/DUdP2fyBaINjJiJy4o94AM8J8ksz/MQjAAAAAElFTkSuQmCC\\' style=\\'height:0.9em;\\'>';}deaficon = '';if(uState.self_deaf || uState.deaf){deaficon='<img src=\\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAABhMAAAYJQE8CCw1AAAAB3RJTUUH5AUGCx077rhJQQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAACNSURBVDjLtZPNCcAgDIUboSs4iXTGLuI2XjpBz87g4fWiENr8iNBAQPR9ef7EbfsjAEQAN4A2UtCcGtyMzFxjwVlyBHAwTRFh52gqHDVnF+6L1XJ/w31cp7YvOX/0xlOJ254qYJ1ZLTAmPWeuDVxARDurfBFR8jovMLEKWxG6c1qB55pEuQOpE8vKz30AhEdNuXK0IugAAAAASUVORK5CYII=\\' style=\\'height:0.9em;\\'>';}spans = document.getElementsByTagName('span');for(i=0;i<spans.length;i++){if(spans[i].innerHTML.startsWith(name)){text = name + muteicon + deaficon;spans[i].innerHTML = text;}}}});"
            self.overlay.page().runJavaScript(tweak)

    def runJS(self, string, retFunc=None):
        if retFunc:
            self.settingWebView.page().runJavaScript(string, retFunc)
        else:
            self.settingWebView.page().runJavaScript(string)

    def applyTweaks(self):
        self.enableConsoleCatcher()
        if self.right:
            self.addCSS(
                'cssrightalign', 'li.voice-state{ direction:rtl; }.avatar{ float:right !important; }.user{ display:flex; }.voice-container{margin-top:30px;}.voice-container:before{position:fixed;right:0px;top:0px;}')
        else:
            self.delCSS('cssrightalign')
        if self.showtitle:
            self.enableShowVoiceTitle()
        if self.mutedeaf:
            self.enableMuteDeaf()
        if self.hideinactive:
            self.enableHideInactive()
        if self.chatresize:
            self.addCSS(
                'cssflexybox', 'div.chat-container { width: 100%; height: 100%; top: 0; left: 0; position: fixed; display: flex; flex-direction: column; } div.chat-container > .messages { box-sizing: border-box; width: 100%; flex: 1; }')
        else:
            self.delCSS('cssflexybox')

    def addCSS(self, name, css):
        if self.overlay:
            js = '(function() { css = document.getElementById(\'%s\'); if (css == null) { css = document.createElement(\'style\'); css.type=\'text/css\'; css.id=\'%s\'; document.head.appendChild(css); } css.innerText=\'%s\';})()' % (name, name, css)
            self.overlay.page().runJavaScript(js)

    def delCSS(self, name):
        if self.overlay:
            js = "(function() { css = document.getElementById('%s'); if(css!=null){ css.parentNode.removeChild(css);} })()" % (
                name)
            self.overlay.page().runJavaScript(js)

    @pyqtSlot()
    def toggleEnabled(self, button=None):
        self.enabled = self.enabledButton.isChecked()
        if self.enabled:
            self.showOverlay()
        else:
            self.hideOverlay()

    @pyqtSlot()
    def toggleTitle(self, button=None):
        self.showtitle = self.showTitle.isChecked()
        if self.showtitle:
            self.enableShowVoiceTitle()

    @pyqtSlot()
    def toggleMuteDeaf(self, button=None):
        self.mutedeaf = self.muteDeaf.isChecked()
        if self.muteDeaf.isChecked():
            self.enableMuteDeaf()

    @pyqtSlot()
    def toggleHideInactive(self, button=None):
        self.hideinactive = self.hideInactive.isChecked()
        if self.hideinactive:
            self.enableHideInactive()

    @pyqtSlot()
    def toggleChatResize(self, button=None):
        self.chatresize = self.chatResize.isChecked()
        self.applyTweaks()

    @pyqtSlot()
    def toggleRightAlign(self, button=None):
        self.right = self.rightAlign.isChecked()
        self.applyTweaks()

    @pyqtSlot()
    def changeValueFL(self):
        self.posXL = self.settingsDistanceFromLeft.value()
        self.moveOverlay()

    @pyqtSlot()
    def changeValueFR(self):
        self.posXR = self.settingsDistanceFromRight.value()
        self.moveOverlay()

    @pyqtSlot()
    def changeValueFT(self):
        self.posYT = self.settingsDistanceFromTop.value()
        self.moveOverlay()

    @pyqtSlot()
    def changeValueFB(self):
        self.posYB = self.settingsDistanceFromBottom.value()
        self.moveOverlay()

    def fillPositionWindowOptions(self):
        self.settingsDistanceFromLeft.valueChanged[int].connect(
            self.changeValueFL)
        self.settingsDistanceFromLeft.setMaximum(self.size.width())
        self.settingsDistanceFromLeft.setValue(self.posXL)
        self.settingsDistanceFromRight.valueChanged[int].connect(
            self.changeValueFR)
        self.settingsDistanceFromRight.setMaximum(self.size.width())
        self.settingsDistanceFromRight.setValue(self.posXR)
        self.settingsDistanceFromTop.valueChanged[int].connect(
            self.changeValueFT)
        self.settingsDistanceFromTop.setMaximum(self.size.height())
        self.settingsDistanceFromTop.setInvertedAppearance(True)
        self.settingsDistanceFromTop.setValue(self.posYT)
        self.settingsDistanceFromBottom.valueChanged[int].connect(
            self.changeValueFB)
        self.settingsDistanceFromBottom.setMaximum(self.size.height())
        self.settingsDistanceFromBottom.setInvertedAppearance(True)
        self.settingsDistanceFromBottom.setValue(self.posYB)

    def populateScreenList(self):
        self.ignoreScreenComboBox = True
        screenList = self.app.screens()
        self.settingsScreen.clear()
        for i, s in enumerate(screenList):
            self.settingsScreen.addItem(s.name())
            if s.name() == self.screenName:
                self.settingsScreen.setCurrentIndex(i)

        self.ignoreScreenComboBox = False
        self.chooseScreen()

    def changeScreen(self, index):
        if not self.ignoreScreenComboBox:
            self.screenName = self.settingsScreen.currentText()
            self.chooseScreen()

    def chooseScreen(self):
        screen = None
        screenList = self.app.screens()
        logger.debug("Discovered screens: %r", [s.name() for s in screenList])

        for s in screenList:
            if s.name() == self.screenName:
                screen = s
                logger.debug("Chose screen %s", screen.name())
                break
        # The chosen screen is not in this list. Drop to primary
        else:
            screen = self.app.primaryScreen()
            logger.warning(
                "Chose screen %r as fallback because %r could not be matched", screen.name(), self.screenName)

        # Fill Info!
        self.size = screen.size()
        self.screenName = s.name()
        self.screenOffset = screen.availableGeometry()
        if self.position:
            self.settingsAspectRatio.updateScreen(
                self.size.width(), self.size.height())
            self.fillPositionWindowOptions()
            self.screenShot(screen)
        self.moveOverlay()

    def showPosition(self):
        if self.position is not None:
            self.position.show()
        else:
            # Positional Settings Window
            self.position = QtWidgets.QWidget()
            self.position.setWindowTitle('Overlay %s Position' % (self.name))
            self.positionbox = QtWidgets.QVBoxLayout()

            # Use a grid to lay out screen & sliders
            self.settingsGridWidget = QtWidgets.QWidget()
            self.settingsGrid = QtWidgets.QGridLayout()

            # Use the custom Aspect widget to keep the whole thing looking
            # as close to the user experience as possible
            self.settingsAspectRatio = AspectRatioWidget(
                self.settingsGridWidget)

            # Grid contents
            self.settingsPreview = ResizingImage()
            self.settingsPreview.setMinimumSize(1, 1)
            sizePolicy = QtWidgets.QSizePolicy(
                QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
            self.settingsPreview.setSizePolicy(sizePolicy)
            self.settingsDistanceFromLeft = QtWidgets.QSlider(
                QtCore.Qt.Horizontal)
            self.settingsDistanceFromRight = QtWidgets.QSlider(
                QtCore.Qt.Horizontal)
            self.settingsDistanceFromTop = QtWidgets.QSlider(
                QtCore.Qt.Vertical)
            self.settingsDistanceFromBottom = QtWidgets.QSlider(
                QtCore.Qt.Vertical)

            # Screen chooser
            self.settingsScreen = QtWidgets.QComboBox()

    #        self.position.setMinimumSize(600,600)
            # Save button
            self.settingSave = QtWidgets.QPushButton("Save")

            # Fill Screens, Choose the screen if config is set
            self.populateScreenList()

            self.settingSave.clicked.connect(self.on_save_position)
            self.settingsScreen.currentIndexChanged.connect(self.changeScreen)

            self.settingsGrid.addWidget(self.settingsPreview, 0, 0)
            self.settingsGrid.addWidget(self.settingsDistanceFromLeft, 1, 0)
            self.settingsGrid.addWidget(self.settingsDistanceFromRight, 2, 0)
            self.settingsGrid.addWidget(self.settingsDistanceFromTop, 0, 1)
            self.settingsGrid.addWidget(self.settingsDistanceFromBottom, 0, 2)
            self.settingsGridWidget.setLayout(self.settingsGrid)
            self.positionbox.addWidget(self.settingsScreen)
            self.positionbox.addWidget(self.settingsAspectRatio)
            self.position.setLayout(self.positionbox)
            self.positionbox.addWidget(self.settingSave)
            self.fillPositionWindowOptions()
            self.position.show()

    def showSettings(self):
        if self.settings is not None:
            self.settings.show()
        else:
            self.settings = QtWidgets.QWidget()
            self.settings.setWindowTitle('Overlay %s Layout' % (self.name))
            self.settingsbox = QtWidgets.QVBoxLayout()
            self.settingWebView = QWebEngineView()
            self.rightAlign = QtWidgets.QCheckBox("Right Align")
            self.muteDeaf = QtWidgets.QCheckBox("Show mute and deafen")
            self.chatResize = QtWidgets.QCheckBox("Large chat box")
            self.showTitle = QtWidgets.QCheckBox("Show room title")
            self.hideInactive = QtWidgets.QCheckBox(
                "Hide voice channel when inactive")
            self.enabledButton = QtWidgets.QCheckBox("Enabled")
            self.settingTakeUrl = QtWidgets.QPushButton("Use this Room")
            self.settingTakeAllUrl = QtWidgets.QPushButton("Use all Rooms")

            self.settings.setMinimumSize(400, 400)
            self.settingTakeUrl.clicked.connect(self.on_click)
            self.settingTakeAllUrl.clicked.connect(self.getAllRooms)
            self.settingWebView.loadFinished.connect(self.skip_stream_button)
            self.rightAlign.stateChanged.connect(self.toggleRightAlign)
            self.rightAlign.setChecked(self.right)
            self.muteDeaf.stateChanged.connect(self.toggleMuteDeaf)
            self.muteDeaf.setChecked(self.mutedeaf)
            self.showTitle.stateChanged.connect(self.toggleTitle)
            self.showTitle.setChecked(self.showtitle)
            self.hideInactive.stateChanged.connect(self.toggleHideInactive)
            self.hideInactive.setChecked(self.hideinactive)
            self.enabledButton.stateChanged.connect(self.toggleEnabled)
            self.enabledButton.setChecked(self.enabled)
            self.chatResize.stateChanged.connect(self.toggleChatResize)
            self.chatResize.setChecked(self.chatresize)

            self.settingWebView.load(QtCore.QUrl(
                "https://streamkit.discord.com/overlay"))

            self.settingsbox.addWidget(self.settingWebView)
            self.settingsbox.addWidget(self.rightAlign)
            self.settingsbox.addWidget(self.muteDeaf)
            self.settingsbox.addWidget(self.chatResize)
            self.settingsbox.addWidget(self.showTitle)
            self.settingsbox.addWidget(self.hideInactive)
            self.settingsbox.addWidget(self.enabledButton)
            self.settingsbox.addWidget(self.settingTakeUrl)
            self.settingsbox.addWidget(self.settingTakeAllUrl)
            self.settings.setLayout(self.settingsbox)
            self.settings.show()

    def screenShot(self, screen):
        screenshot = screen.grabWindow(0)
        self.settingsPreview.setImage(screenshot)
        self.settingsPreview.setContentsMargins(0, 0, 0, 0)

    def showOverlay(self):
        if self.overlay:
            return
        self.overlay = QWebEngineView()
        self.overlay.page().setBackgroundColor(QtCore.Qt.transparent)
        self.overlay.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
        self.overlay.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents, True)
        self.overlay.setWindowFlags(
            QtCore.Qt.X11BypassWindowManagerHint |
            QtCore.Qt.FramelessWindowHint |
            QtCore.Qt.WindowStaysOnTopHint |
            QtCore.Qt.WindowTransparentForInput |
            QtCore.Qt.WindowDoesNotAcceptFocus |
            QtCore.Qt.NoDropShadowWindowHint |
            QtCore.Qt.WindowSystemMenuHint |
            QtCore.Qt.WindowMinimizeButtonHint
        )
        self.overlay.loadFinished.connect(self.applyTweaks)
        self.overlay.load(QtCore.QUrl(self.url))

        self.overlay.setStyleSheet("background:transparent;")
        self.overlay.show()
        self.moveOverlay()

    def hideOverlay(self):
        if self.overlay:
            self.overlay.close()
            self.overlay = None

    def delete(self):
        self.hideOverlay()
        if self.settings:
            self.settings.close()
        if self.position:
            self.position.close()
        self.overlay = None

    def getAllRooms(self):
        getChannel = "[window.channels, window.guilds]"
        self.runJS(getChannel, self.gotAllRooms)

    def gotAllRooms(self, message):
        sep = ''
        url = ''
        for chan in message[0]:
            if chan['type'] == 2:
                url += sep+"https://streamkit.discord.com/overlay/voice/%s/%s?icon=true&online=true&logo=white&text_color=%%23ffffff&text_size=14&text_outline_color=%%23000000&text_outline_size=0&text_shadow_color=%%23000000&text_shadow_size=0&bg_color=%%231e2124&bg_opacity=0.95&bg_shadow_color=%%23000000&bg_shadow_size=0&limit_speaking=false&small_avatars=false&hide_names=false&fade_chat=0" % (message[1], chan[
                    'id'])
                sep = ','

        if self.overlay:
            self.overlay.load(QtCore.QUrl(url))
        self.url = url
        self.save()
        self.settings.close()
        self.settings = None
コード例 #7
0
ファイル: browser.py プロジェクト: GalaxyGroot/anki-plugins
class AwBrowser(QDialog):
    """
        Customization and configuration of a web browser to run within Anki
    """

    SINGLETON = None
    TITLE = 'Anki :: Web Browser Addon'

    _parent = None
    _fields = []
    _selectionHandler = None
    _web = None
    _context = None
    _lastAssignedField = None
    infoList = []
    providerList = []

    def __init__(self, myParent):
        QDialog.__init__(self, None)
        self._parent = myParent
        self.setupUI()

        if myParent:

            def wrapClose(fn):
                def clozeBrowser(evt):
                    self.close()
                    fn(evt)

                return clozeBrowser

            myParent.closeEvent = wrapClose(myParent.closeEvent)

    def setupUI(self):
        self.setWindowTitle(AwBrowser.TITLE)
        self.setWindowFlags(Qt.WindowMinMaxButtonsHint
                            | Qt.WindowCloseButtonHint)
        self.setGeometry(450, 200, 800, 450)
        self.setMinimumWidth(640)
        self.setMinimumHeight(450)
        self.setStyleSheet(Style.DARK_BG)

        mainLayout = QVBoxLayout()
        mainLayout.setContentsMargins(0, 0, 0, 0)
        mainLayout.setSpacing(0)
        self.setLayout(mainLayout)

        self._web = QWebEngineView(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.page().urlChanged.connect(self.onPageChange)

        # -------------------- Top / toolbar ----------------------
        navtbar = QToolBar("Navigation")
        navtbar.setIconSize(QSize(16, 16))
        mainLayout.addWidget(navtbar)

        backBtn = QAction(
            QtGui.QIcon(os.path.join(CWD, 'assets', 'arrow-back.png')), "Back",
            self)
        backBtn.setStatusTip("Back to previous page")
        backBtn.triggered.connect(self._web.back)
        navtbar.addAction(backBtn)

        self.forwardBtn = QAction(
            QtGui.QIcon(os.path.join(CWD, 'assets', 'arrow-forward.png')),
            "Forward", self)
        self.forwardBtn.setStatusTip("Next visited page")
        self.forwardBtn.triggered.connect(self._web.forward)
        navtbar.addAction(self.forwardBtn)

        refreshBtn = QAction(
            QtGui.QIcon(os.path.join(CWD, 'assets', 'reload.png')), "Reload",
            self)
        refreshBtn.setStatusTip("Reload")
        refreshBtn.triggered.connect(self._web.reload)
        navtbar.addAction(refreshBtn)

        self.createProvidersMenu(navtbar)

        self._itAddress = QtWidgets.QLineEdit(self)
        self._itAddress.setObjectName("itSite")
        self._itAddress.setStyleSheet('background-color: #F5F5F5;')
        self._itAddress.returnPressed.connect(self._goToAddress)
        navtbar.addWidget(self._itAddress)

        cbGo = QAction(QtGui.QIcon(os.path.join(CWD, 'assets', 'go-icon.png')),
                       "Go", self)
        cbGo.setObjectName("cbGo")
        navtbar.addAction(cbGo)
        cbGo.triggered.connect(self._goToAddress)

        self.stopBtn = QAction(
            QtGui.QIcon(os.path.join(CWD, 'assets', 'stop.png')), "Stop", self)
        self.stopBtn.setStatusTip("Stop loading")
        self.stopBtn.triggered.connect(self._web.stop)
        navtbar.addAction(self.stopBtn)
        # -------------------- Center ----------------------
        mainLayout.addWidget(self._web)
        # -------------------- Bottom bar ----------------------

        bottomWidget = QtWidgets.QWidget(self)
        bottomWidget.setFixedHeight(30)

        bottomLayout = QtWidgets.QHBoxLayout(bottomWidget)
        bottomLayout.setObjectName("bottomLayout")
        bottomWidget.setStyleSheet('color: #FFF;')

        lbSite = QtWidgets.QLabel(bottomWidget)
        lbSite.setObjectName("label")
        lbSite.setText("Context: ")
        lbSite.setFixedWidth(70)
        lbSite.setStyleSheet('font-weight: bold;')
        bottomLayout.addWidget(lbSite)

        self.ctxWidget = QtWidgets.QLabel(bottomWidget)
        self.ctxWidget.width = 300
        self.ctxWidget.setStyleSheet('text-align: left;')
        bottomLayout.addWidget(self.ctxWidget)

        self._loadingBar = QtWidgets.QProgressBar(bottomWidget)
        self._loadingBar.setFixedWidth(100)
        self._loadingBar.setProperty("value", 100)
        self._loadingBar.setObjectName("loadingBar")
        bottomLayout.addWidget(self._loadingBar)

        mainLayout.addWidget(bottomWidget)

        if cfg.getConfig().browserAlwaysOnTop:
            self.setWindowFlags(Qt.WindowStaysOnTopHint)

    @classmethod
    def singleton(cls, parent):
        if not cls.SINGLETON:
            cls.SINGLETON = AwBrowser(parent)
        return cls.SINGLETON

    def formatTargetURL(self, website: str, query: str = ''):
        return website.format(urllib.parse.quote(query, encoding='utf8'))

    @exceptionHandler
    def open(self, website, query: str):
        """
            Loads a given page with its replacing part with its query, and shows itself
        """

        self._context = query
        self._updateContextWidget()
        target = self.formatTargetURL(website, query)
        self._web.load(QUrl(target))
        self._itAddress.setText(target)

        self.show()
        self.raise_()
        return self._web

    def unload(self):
        try:
            self._web.setHtml(BLANK_PAGE)
            self._itAddress.setText('about:blank')
        except (RuntimeError) as err:
            pass

    def onClose(self):
        self._parent = None
        self._web.close()
        self.close()

    def onStartLoading(self):
        self.stopBtn.setEnabled(True)
        self._loadingBar.setProperty("value", 1)

    def onProgress(self, prog):
        self._loadingBar.setProperty("value", prog)

    def onLoadFinish(self, result):
        self.stopBtn.setDisabled(True)
        self._loadingBar.setProperty("value", 100)

        if not result:
            Feedback.log('No result on loading page! ')

    def _goToAddress(self):
        q = QUrl(self._itAddress.text())
        if q.scheme() == "":
            q.setScheme("http")

        self._web.load(q)
        self._web.show()

    def onPageChange(self, url):
        if url and url.toString().startswith('http'):
            self._itAddress.setText(url.toString())
        self.forwardBtn.setEnabled(self._web.history().canGoForward())

    def welcome(self):
        self._web.setHtml(WELCOME_PAGE)
        self._itAddress.setText('about:blank')
        self.show()
        self.raise_()

    def _updateContextWidget(self):
        self.ctxWidget.setText(self._context)

# ---------------------------------------------------------------------------------

    def createProvidersMenu(self, parentWidget):
        providerBtn = QAction(
            QtGui.QIcon(os.path.join(CWD, 'assets', 'gear-icon.png')),
            "Providers", parentWidget)
        providerBtn.setStatusTip("Search with Provider")
        providerBtn.triggered.connect(
            lambda: self.newProviderMenu(providerBtn))
        parentWidget.addAction(providerBtn)

    def newProviderMenu(self, parentBtn):
        ctx = ProviderSelectionController()
        ctx.showCustomMenu(parentBtn.parentWidget(), self.reOpenSameQuery)

    @exceptionHandler
    def reOpenSameQuery(self, website):
        self.open(website, self._context)

# ------------------------------------ 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
        """
        def _processMenuSelection():
            self._lastAssignedField = field
            self._selectionHandler(field, value, isLink)

        return _processMenuSelection

    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._selectionHandler):
            return self.createInfoMenu(evt)

        isLink = False
        value = None
        if self._web.selectedText():
            isLink = False
            value = self._web.selectedText()
        else:
            if (self._web.page().contextMenuData().mediaType()
                    == QWebEngineContextMenuData.MediaTypeImage
                    and self._web.page().contextMenuData().mediaUrl()):
                isLink = True
                value = self._web.page().contextMenuData().mediaUrl()
                Feedback.log('Link: ' + value.toString())
                Feedback.log('toLocal: ' + value.toLocalFile())

                if not self._checkSuffix(value):
                    return

        if not value:
            Feedback.log('No value')
            return self.createInfoMenu(evt)

        if QApplication.keyboardModifiers() == Qt.ControlModifier:
            if self._assignToLastField(value, isLink):
                return

        self.createCtxMenu(value, isLink, evt)

    def _checkSuffix(self, value):
        if value and not value.toString().endswith(
            ("jpg", "jpeg", "png", "gif")):
            msgLink = value.toString()
            if len(value.toString()) < 80:
                msgLink = msgLink[:50] + '...' + msgLink[50:]
            answ = QMessageBox.question(
                self, 'Anki support',
                """This link may not be accepted by Anki: \n\n "%s" \n
Usually the suffix should be one of 
(jpg, jpeg, png, gif).
Try it anyway? """ % msgLink, QMessageBox.Yes | QMessageBox.No)

            if answ != QMessageBox.Yes:
                return False

        return True

    def createCtxMenu(self, value, isLink, evt):
        'Creates and configures the menu itself'

        m = QMenu(self)
        m.addAction(QAction('Copy', m, triggered=lambda: self._copy(value)))
        m.addSeparator()

        labelAct = QAction(Label.BROWSER_ASSIGN_TO, m)
        labelAct.setDisabled(True)
        m.addAction(labelAct)
        # 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))
            m.addAction(act)

        # m.addMenu(sub)
        action = m.exec_(self.mapToGlobal(evt.pos()))

    def createInfoMenu(self, evt):
        'Creates and configures a menu with only some information'
        m = QMenu(self)
        for item in self.infoList:
            act = QAction(item)
            act.setEnabled(False)
            m.addAction(act)
        action = m.exec_(self.mapToGlobal(evt.pos()))

    def _assignToLastField(self, value, isLink):
        'Tries to set the new value to the same field used before, if set...'

        if self._lastAssignedField:
            if self._lastAssignedField in self._fields:
                self._selectionHandler(self._lastAssignedField, value, isLink)
                return True
            else:
                self._lastAssignedField = None
        return False

    def _copy(self, value):
        if not value:
            return
        clip = QApplication.clipboard()
        clip.setText(value if isinstance(value, str) else value.toString())

    def load(self, qUrl):
        self._web.load(qUrl)

#   ----------------- getter / setter  -------------------

    def setFields(self, fList):
        self._fields = fList

    def setSelectionHandler(self, value):
        self._selectionHandler = value
コード例 #8
0
class ChromiumBrowserTab(BrowserTab):
    def __init__(self, render_options, verbosity):
        super().__init__(render_options, verbosity)
        self.profile = QWebEngineProfile()  # don't share cookies
        self.web_page = ChromiumWebPage(self.profile)
        self.web_view = QWebEngineView()
        self.web_view.setPage(self.web_page)

        self.web_view.setAttribute(Qt.WA_DeleteOnClose, True)

        # TODO: is it ok? :)
        # self.web_view.setAttribute(Qt.WA_DontShowOnScreen, True)

        # FIXME: required for screenshots?
        # Also, without .show() in JS window.innerWidth/innerHeight are zeros
        self.web_view.show()

        self._setup_webpage_events()
        self._set_default_webpage_options()
        self._html_d = None

        # ensure that default window size is not 640x480.
        self.set_viewport(defaults.VIEWPORT_SIZE)

    def _setup_webpage_events(self):
        self._load_finished = WrappedSignal(self.web_view.loadFinished)
        self._render_terminated = WrappedSignal(self.web_view.renderProcessTerminated)

        self.web_view.renderProcessTerminated.connect(self._on_render_terminated)
        self.web_view.loadFinished.connect(self._on_load_finished)
        # main_frame.urlChanged.connect(self._on_url_changed)
        # main_frame.javaScriptWindowObjectCleared.connect(
        #     self._on_javascript_window_object_cleared)
        # self.logger.add_web_page(self.web_page)

    def _set_default_webpage_options(self):
        """ Set QWebPage options. TODO: allow to customize defaults. """
        settings = self.web_page.settings()
        settings.setAttribute(QWebEngineSettings.ScreenCaptureEnabled, True)
        settings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, False)
        settings.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)

        # TODO: requires Qt 5.10
        # settings.setAttribute(QWebEngineSettings.ShowScrollBars, False)

        # TODO
        # if self.visible:
        #     settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)

        # TODO: options
        # self.set_js_enabled(True)
        # self.set_plugins_enabled(defaults.PLUGINS_ENABLED)
        # self.set_request_body_enabled(defaults.REQUEST_BODY_ENABLED)
        # self.set_response_body_enabled(defaults.RESPONSE_BODY_ENABLED)
        # self.set_indexeddb_enabled(defaults.INDEXEDDB_ENABLED)
        # self.set_webgl_enabled(defaults.WEBGL_ENABLED)
        # self.set_html5_media_enabled(defaults.HTML5_MEDIA_ENABLED)
        # self.set_media_source_enabled(defaults.MEDIA_SOURCE_ENABLED)

    def go(self, url, callback, errback):
        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_view.load(QUrl(url))

    @skip_if_closing
    def _on_content_ready(self, ok, callback, errback, callback_id):
        """
        This method is called when a QWebEnginePage finishes loading.
        """
        self.logger.log("loadFinished: disconnecting callback %s" % callback_id,
                        min_level=3)
        self._load_finished.disconnect(callback_id)
        if ok:
            callback()
        else:
            error_info = RenderErrorInfo(
                type='Unknown',
                code=0,
                text="loadFinished ok=False",
                url=self.web_view.url().toString()
            )
            errback(error_info)

    def _on_load_finished(self, ok):
        self.logger.log("loadFinished, ok=%s" % ok, min_level=2)

    def _on_render_terminated(self, status, code):
        status_details = RenderProcessTerminationStatus.get(status, 'unknown')
        self.logger.log("renderProcessTerminated: %s (%s), exit_code=%s" % (
            status, status_details, code), min_level=1)

    def html(self):
        """ Return HTML of the current main frame """
        self.logger.log("getting HTML", min_level=2)
        if self._html_d is not None:
            self.logger.log("HTML is already requested", min_level=1)
            return self._html_d
        self._html_d = defer.Deferred()
        self.web_view.page().toHtml(self._on_html_ready)
        return self._html_d

    def _on_html_ready(self, html):
        self.logger.log("HTML ready", min_level=2)
        self._html_d.callback(html)
        self._html_d = None

    def png(self, width=None, height=None, b64=False, render_all=False,
            scale_method=None, region=None):
        """ Return screenshot in PNG format """
        # FIXME: move to base class
        self.logger.log(
            "Getting PNG: width=%s, height=%s, "
            "render_all=%s, scale_method=%s, region=%s" %
            (width, height, render_all, scale_method, region), min_level=2)
        if render_all:
            raise ValueError("render_all=True is not supported yet")

        image = self._get_image('PNG', width, height, render_all,
                                scale_method, region=region)
        result = image.to_png()
        if b64:
            result = base64.b64encode(result).decode('utf-8')
        # self.store_har_timing("_onPngRendered")
        return result

    def jpeg(self, width=None, height=None, b64=False, render_all=False,
             scale_method=None, quality=None, region=None):
        """ Return screenshot in JPEG format. """
        # FIXME: move to base class
        self.logger.log(
            "Getting JPEG: width=%s, height=%s, "
            "render_all=%s, scale_method=%s, quality=%s, region=%s" %
            (width, height, render_all, scale_method, quality, region),
            min_level=2)
        if render_all:
            raise ValueError("render_all=True is not supported yet")

        image = self._get_image('JPEG', width, height, render_all,
                                scale_method, region=region)
        result = image.to_jpeg(quality=quality)
        if b64:
            result = base64.b64encode(result).decode('utf-8')
        # self.store_har_timing("_onJpegRendered")
        return result

    def _get_image(self, image_format, width, height, render_all,
                   scale_method, region):
        renderer = QtChromiumScreenshotRenderer(
            self.web_page, self.logger, image_format,
            width=width, height=height, scale_method=scale_method,
            region=region)
        return renderer.render_qwebpage()

    def set_viewport(self, size, raise_if_empty=False):
        """
        Set viewport size.
        If size is "full" viewport size is detected automatically.
        If can also be "<width>x<height>".

        FIXME: Currently the implementation just resizes the window, which
        causes Splash to crash on large sizes(?).
        Actully it is not changing the viewport.

        XXX: As an effect, this function changes window.outerWidth/outerHeight,
        while in Webkit implementation window.innerWidth/innerHeight
        is changed.
        """
        if size == 'full':
            size = self.web_page.contentsSize()
            self.logger.log("Contents size: %s" % size, min_level=2)
            if size.isEmpty():
                if raise_if_empty:
                    raise RuntimeError("Cannot detect viewport size")
                else:
                    size = defaults.VIEWPORT_SIZE
                    self.logger.log("Viewport is empty, falling back to: %s" %
                                    size)

        if not isinstance(size, (QSize, QSizeF)):
            validate_size_str(size)
            size = parse_size(size)
        w, h = int(size.width()), int(size.height())

        # XXX: it was crashing with large windows, but then the problem
        # seemed to go away. Need to keep an eye on it.
        # # FIXME: don't resize the window?
        # # FIXME: figure out exact limits
        # MAX_WIDTH = 1280
        # MAX_HEIGHT = 1920
        #
        # if w > MAX_WIDTH:
        #     raise RuntimeError("Width {} > {} is currently prohibited".format(
        #         w, MAX_WIDTH
        #     ))
        #
        # if h > MAX_HEIGHT:
        #     raise RuntimeError("Height {} > {} is currently prohibited".format(
        #         h, MAX_HEIGHT
        #     ))
        self.web_view.resize(w, h)

        # self._force_relayout()
        self.logger.log("viewport size is set to %sx%s" % (w, h), min_level=2)
        self.logger.log("real viewport size: %s" % self.web_view.size(), min_level=2)
        return w, h

    def stop_loading(self):
        self.logger.log("stop_loading", min_level=2)
        self.web_view.stop()

    @skip_if_closing
    def close(self):
        """ Destroy this tab """
        super().close()
        self.web_view.stop()
        self.web_view.close()
        self.web_page.deleteLater()
        self.web_view.deleteLater()