Приложение PyQT5 с Selenium не отвечает - не отображается индикатор выполнения и не выводится текст в консоли до завершения задачи

Iframes больше не требуется для загрузки файлов через ajax. Я недавно сделал это сам. Проверьте эти страницы:

Использование загрузки файлов HTML5 с помощью AJAX и jQuery

http://dev.w3.org/2006/webapi / FileAPI / # FileReader-interface

Обновил ответ и очистил его. Используйте функцию getSize для проверки размера или использования функции getType для проверки типов. Добавлен progressbar html и код css.

var Upload = function (file) {
    this.file = file;
};

Upload.prototype.getType = function() {
    return this.file.type;
};
Upload.prototype.getSize = function() {
    return this.file.size;
};
Upload.prototype.getName = function() {
    return this.file.name;
};
Upload.prototype.doUpload = function () {
    var that = this;
    var formData = new FormData();

    // add assoc key values, this will be posts values
    formData.append("file", this.file, this.getName());
    formData.append("upload_file", true);

    $.ajax({
        type: "POST",
        url: "script",
        xhr: function () {
            var myXhr = $.ajaxSettings.xhr();
            if (myXhr.upload) {
                myXhr.upload.addEventListener('progress', that.progressHandling, false);
            }
            return myXhr;
        },
        success: function (data) {
            // your callback here
        },
        error: function (error) {
            // handle error
        },
        async: true,
        data: formData,
        cache: false,
        contentType: false,
        processData: false,
        timeout: 60000
    });
};

Upload.prototype.progressHandling = function (event) {
    var percent = 0;
    var position = event.loaded || event.position;
    var total = event.total;
    var progress_bar_id = "#progress-wrp";
    if (event.lengthComputable) {
        percent = Math.ceil(position / total * 100);
    }
    // update progressbars classes so it fits your code
    $(progress_bar_id + " .progress-bar").css("width", +percent + "%");
    $(progress_bar_id + " .status").text(percent + "%");
};

Как использовать класс загрузки

//Change id to your id
$("#ingredient_file").on("change", function (e) {
    var file = $(this)[0].files[0];
    var upload = new Upload(file);

    // maby check size or type here with upload.getSize() and upload.getType()

    // execute upload
    upload.doUpload();
});

Progressbar html code

0%

Progressbar css code

#progress-wrp {
  border: 1px solid #0099CC;
  padding: 1px;
  position: relative;
  height: 30px;
  border-radius: 3px;
  margin: 10px;
  text-align: left;
  background: #fff;
  box-shadow: inset 1px 3px 6px rgba(0, 0, 0, 0.12);
}

#progress-wrp .progress-bar {
  height: 100%;
  border-radius: 3px;
  background-color: #f39ac7;
  width: 0;
  box-shadow: inset 1px 1px 10px rgba(0, 0, 0, 0.11);
}

#progress-wrp .status {
  top: 3px;
  left: 50%;
  position: absolute;
  display: inline-block;
  color: #000000;
}

1
задан John K 22 March 2019 в 23:52
поделиться

1 ответ

Прежде всего у вас есть следующие плохие практики программирования:

  • Вы путаете бизнес-логику с GUI.
  • Имена переменных не описывают использование переменных.
  • Много кода в файле.

С другой стороны, при переходе на Qt у вас есть следующие ошибки:

  • Вы не должны изменять GUI из другого потока, так как GUI не является безопасным для протектора, вы должны использовать сигналы, события и т. д.
  • Вы должны использовать макеты для графического интерфейса, чтобы адаптироваться при изменении размера.

С другой стороны, я не проверял ваш код, но я вижу, что одна из ваших ошибок -

t = threading.Thread(target=self.generate())

, так как вы вызываете функцию вместо передачи ее в функцию, она должна be

t = threading.Thread(target=self.generate)

Учитывая вышеизложенное, я реализовал следующее:

fuse_ui.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'fuse.ui'
#
# Created by: PyQt5 UI code generator 5.12.1
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Fuse(object):
    def setupUi(self, Fuse):
        Fuse.setObjectName("Fuse")
        Fuse.resize(556, 513)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(Fuse.sizePolicy().hasHeightForWidth())
        Fuse.setSizePolicy(sizePolicy)
        self.verticalLayout = QtWidgets.QVBoxLayout(Fuse)
        self.verticalLayout.setObjectName("verticalLayout")
        spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.verticalLayout.addItem(spacerItem)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        spacerItem1 = QtWidgets.QSpacerItem(80, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_2.addItem(spacerItem1)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.userNameLabel = QtWidgets.QLabel(Fuse)
        self.userNameLabel.setObjectName("userNameLabel")
        self.gridLayout.addWidget(self.userNameLabel, 0, 0, 1, 1)
        self.userNameLineEdit = QtWidgets.QLineEdit(Fuse)
        self.userNameLineEdit.setObjectName("userNameLineEdit")
        self.gridLayout.addWidget(self.userNameLineEdit, 0, 1, 1, 1)
        self.passwordLabel = QtWidgets.QLabel(Fuse)
        self.passwordLabel.setObjectName("passwordLabel")
        self.gridLayout.addWidget(self.passwordLabel, 1, 0, 1, 1)
        self.passwordLineEdit = QtWidgets.QLineEdit(Fuse)
        self.passwordLineEdit.setObjectName("passwordLineEdit")
        self.gridLayout.addWidget(self.passwordLineEdit, 1, 1, 1, 1)
        self.invalidLabel = QtWidgets.QLabel(Fuse)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.invalidLabel.sizePolicy().hasHeightForWidth())
        self.invalidLabel.setSizePolicy(sizePolicy)
        self.invalidLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.invalidLabel.setObjectName("invalidLabel")
        self.gridLayout.addWidget(self.invalidLabel, 2, 1, 1, 1)
        self.widget = QtWidgets.QWidget(Fuse)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
        self.widget.setSizePolicy(sizePolicy)
        self.widget.setObjectName("widget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.submitButton = QtWidgets.QPushButton(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.submitButton.sizePolicy().hasHeightForWidth())
        self.submitButton.setSizePolicy(sizePolicy)
        self.submitButton.setObjectName("submitButton")
        self.horizontalLayout.addWidget(self.submitButton)
        self.resetButton = QtWidgets.QPushButton(self.widget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.resetButton.sizePolicy().hasHeightForWidth())
        self.resetButton.setSizePolicy(sizePolicy)
        self.resetButton.setObjectName("resetButton")
        self.horizontalLayout.addWidget(self.resetButton)
        self.gridLayout.addWidget(self.widget, 3, 1, 1, 1)
        self.horizontalLayout_2.addLayout(self.gridLayout)
        spacerItem2 = QtWidgets.QSpacerItem(80, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_2.addItem(spacerItem2)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        spacerItem3 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.verticalLayout.addItem(spacerItem3)
        self.logTextEdit = QtWidgets.QTextEdit(Fuse)
        self.logTextEdit.setObjectName("logTextEdit")
        self.verticalLayout.addWidget(self.logTextEdit)
        self.progressBar = QtWidgets.QProgressBar(Fuse)
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName("progressBar")
        self.verticalLayout.addWidget(self.progressBar)

        self.retranslateUi(Fuse)
        QtCore.QMetaObject.connectSlotsByName(Fuse)

    def retranslateUi(self, Fuse):
        _translate = QtCore.QCoreApplication.translate
        Fuse.setWindowTitle(_translate("Fuse", "Dialog"))
        self.userNameLabel.setText(_translate("Fuse", "<html><head/><body><p><span style=\" font-weight:600;\">User Name</span></p></body></html>"))
        self.passwordLabel.setText(_translate("Fuse", "<html><head/><body><p><span style=\" font-weight:600;\">Password</span></p></body></html>"))
        self.invalidLabel.setText(_translate("Fuse", "<html><head/><body><p><span style=\" font-size:7pt; color:red; font-weight:600;\">Invalid User Name or Password</span></p></body></html>"))
        self.submitButton.setText(_translate("Fuse", "Submit"))
        self.resetButton.setText(_translate("Fuse", "Reset"))




if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Fuse = QtWidgets.QDialog()
    ui = Ui_Fuse()
    ui.setupUi(Fuse)
    Fuse.show()
    sys.exit(app.exec_())

fusi_worker.py

import os
import time

from selenium import webdriver
from selenium.common.exceptions import TimeoutException,NoSuchElementException,ElementClickInterceptedException
from selenium.webdriver.support.ui import WebDriverWait,Select
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
from selenium.webdriver.common.action_chains import ActionChains

from PyQt5 import QtCore

import pandas as pd

class FusiWorker(QtCore.QObject):
    progressChanged = QtCore.pyqtSignal(int)
    logSignal = QtCore.pyqtSignal(str)
    invalidSignal = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()

    @QtCore.pyqtSlot(str, str)
    def start(self, username, password):

        options = Options()
        options.add_argument("--headless")
        profile = FirefoxProfile()
        driver_path = os.path.expandvars('%userprofile%\\Desktop\\RPA\\Tools\\geckodriver.exe')
        driver = webdriver.Firefox(firefox_profile=profile, options=options, executable_path=driver_path)
        driver.get("https://example.com/Login.aspx")
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "loginBtnn")));
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')

        search_field = driver.find_element_by_id("txtUserName")
        search_field.clear()
        search_field.send_keys(password)
        time.sleep(5)
        driver.find_element_by_id("BtnLogin").click()
        self.logSignal.emit('Clicked on Login')
        time.sleep(5)
        try:
            element = driver.find_element_by_xpath("//span[contains(@id,'lblFailure')]")
            if element.text == "Invalid User Name or Password":
                self.logSignal.emit("Invalid User Name or Password")
                self.invalidSignal.emit()
                driver.quit()
                driver.close()
        except NoSuchElementException:
            self.logSignal.emit("Correct User Name or Password")
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.XPATH, "/html/body/form/div[4]/a/img")));
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')    
        driver.find_element_by_xpath("/html/body/form/div[4]/a/img").click()
        self.logSignal('Clicked Product Links')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "lnkFuse")));
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        driver.find_element_by_id("lnkFuse").click()
        self.logSignal.emit('Clicked on Fuse Link')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.XPATH, "//span[@class='Invoice Processing'][contains(.,'Invoice Processing')]")));
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        element_to_hover_over = driver.find_element_by_xpath("//span[@class='Invoice Processing'][contains(.,'Invoice Processing')]")
        hover = ActionChains(driver).move_to_element(element_to_hover_over)
        hover.perform()
        self.logSignal.emit('Clicked on Invoice Processing')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.XPATH, "//a[@href='../../RS/Batch/AuditInvoice.aspx']")))
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        driver.find_element_by_xpath("//a[@href='../../RS/Batch/AuditInvoice.aspx']").click()
        self.logSignal.emit('Clicked on Audit Invoices')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "MainContent_ddlSearchInvoiceStatus")))
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        invoice_audit = 'Audited' 
        InvoiceStatus = Select(driver.find_element_by_id("MainContent_ddlSearchInvoiceStatus"))
        for option in InvoiceStatus.options:
            option_text = option.text
            if invoice_audit in option_text:
                option.click()
                break
        self.logSignal.emit('Selected Audited from Dropdown')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "MainContent_btnSearch")))
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        driver.find_element_by_id('MainContent_btnSearch').click() 
        self.logSignal.emit('Clicked on Search Button')
        try:
            WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "MainContent_ddlItemsPerPage")))
        except TimeoutException:
            self.logSignal.emit('Timed out waiting for page to load')
        element = driver.find_element_by_id('MainContent_ddlItemsPerPage')
        element.location_once_scrolled_into_view
        driver.find_element_by_id('MainContent_ddlItemsPerPage').click() 
        items_perpage = '500' 
        records_list = Select(driver.find_element_by_id("MainContent_ddlItemsPerPage"))
        for option in records_list.options:
            option_text = option.text
            if items_perpage in option_text:
                option.click()
                break
        time.sleep(5)
        self.logSignal.emit('Selected Max items per page')
        list_links = driver.find_elements_by_partial_link_text('Generate')
        time.sleep(5)
        id_list = []
        for i in list_links:
            data = i.get_attribute('id')
            self.logSignal.emit(data)
            id_list.append(data)
            # TODO
            id_df = pd.DataFrame(id_list)
            self.logSignal.emit(id_df.to_string())
        for row in id_df.values:
            row_val = str(row)[2:-2]
            try:
                WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, "MainContent_gvAuditInvoice")))
                self.logSignal.emit('Element Found')
                element = driver.find_element_by_id(row_val)
                element.location_once_scrolled_into_view
                WebDriverWait(driver, 60000).until(EC.element_to_be_clickable((By.ID, row_val)))
                driver.find_element_by_id(row_val).click()
                localtime = time.strftime("%m-%d-%Y  %I:%M:%S")
                self.logSignal.emit(row_val + "|" + 'Clicked' + '|' + localtime)
                time.sleep(5)
            except ElementClickInterceptedException:
                continue
                self.logSignal.emit('Timed out waiting for page to load')
            time.sleep(5)
        self.logSignal.emit('Completed')
        self.finished.emit()

    @QtCore.pyqtSlot()
    def load_data(self):
        completed = 0
        while completed < 100:
            completed += 0.0001
            self.progressChanged.emit(completed)
            time.sleep(0.01)

main.py

import threading
from PyQt5 import QtCore, QtGui, QtWidgets

from fuse_worker import FusiWorker
from fuse_ui import Ui_Fuse

class Fuse(QtWidgets.QDialog, Ui_Fuse):
    def __init__(self, parent=None):
        super(Fuse, self).__init__(parent)
        self.setupUi(self)
        self.setWindowFlags(QtCore.Qt.CustomizeWindowHint | 
                             QtCore.Qt.MSWindowsFixedSizeDialogHint |
                             QtCore.Qt.WindowCloseButtonHint | 
                             QtCore.Qt.WindowMinimizeButtonHint)
        self.init()
        self.connections()

    def init(self):
        self._worker = FusiWorker()
        self.reset()
        thread = QtCore.QThread(self)
        thread.start()
        self._worker.moveToThread(thread)
        self.progressBar.setValue(0)

    def connections(self):
        self.submitButton.clicked.connect(self.submit)
        self.resetButton.clicked.connect(self.reset)
        self._worker.logSignal.connect(self.logTextEdit.insertPlainText)
        self._worker.invalidSignal.connect(self.invalidate)
        self._worker.finished.connect(self.reset)
        self._worker.progressChanged.connect(self.progressBar.setValue)
        self.progressBar.setValue(0)

    @QtCore.pyqtSlot()
    def submit(self):
        username = self.userNameLineEdit.text()
        password = self.passwordLineEdit.text()
        self.logTextEdit.clear()
        self.userNameLineEdit.setEnabled(False)
        self.passwordLineEdit.setEnabled(False)
        threading.Thread(target=self._worker.start, args=(username, password,), daemon=True).start()
        threading.Thread(target=self._worker.load_data, daemon=True).start()

    @QtCore.pyqtSlot()
    def reset(self):
         self.userNameLineEdit.clear()
         self.passwordLineEdit.clear()
         self.invalidLabel.hide()
         self.userNameLineEdit.setEnabled(True)
         self.passwordLineEdit.setEnabled(True)
         self.submitButton.setEnabled(True)

    @QtCore.pyqtSlot()
    def invalidate(self):
        self.invalidLabel.show()
        self.reset()

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Fuse()
    w.show()
    sys.exit(app.exec_())

Эти файлы должны находиться в одной папке:

├── fuse_ui.py
├── fuse_worker.py
└── main.py
0
ответ дан eyllanesc 22 March 2019 в 23:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: