Зависание дочернего окна приложения ProgressBar

e8s22

Новичок
Пользователь
Июн 24, 2020
7
0
1
Здраствуйте, подскажите как исправить проблему с запуском дочернего окна.
При выборе в первом окне Checkbox и нажатию на кнопку "Установить" должно открываться дочернее окно с Прогрессбаром, но дочернее окно зависает.

Код:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QWidget, QDialog, QAction, QProgressBar, QMessageBox, QWidget, QMainWindow, QApplication
import subprocess
import threading
import time
import sys

class window_2(QWidget):
    #def __init__(self):
        #super().__init__()
        #self.setupUi()
    def setupUi(self):
        self.w2 = window_2()
        self.w2.resize(250, 300)
        self.w2.setWindowTitle('Прогресс бар')
        self.w2.progressBar = QProgressBar(self) 
        self.w2.progressBar.setGeometry(0, 0, 300, 25)
        self.w2.progressBar.setProperty("value", 0)
        self.w2.progressBar.setMaximum(100)
        self.w2.show()
        #self.w2.exec_()
        #self.progressBar  = QProgressBar(self)
        #self.progressBar.setGeometry(30,40,200,25)
        #self.progressBar.setProperty("value", 0)

        #self.timer = QBasicTimer()
        self.step = 0
class window(QMainWindow):
    def __init__(self):
        super().__init__() 
    def setupUi(self):
        window.resize(330, 259)
        quit = QAction("Quit", None )
        quit.triggered.connect(self.closeEvent)
        self.centralwidget = QtWidgets.QWidget(window)
        self.centralwidget.setObjectName("centralwidget")
        #self.progressBar  = QtWidgets.QProgressBar(self)
        #self.progressBar.setGeometry(30,40,200,25)
        #self.progressBar.setProperty("value", 0)
        #self.progressBar.setObjectName("progressBar")
        #Кнопка Установить
        self.Btn1 = QtWidgets.QPushButton(self.centralwidget)
        self.Btn1.setCheckable(False)
        self.Btn1.toggle()
        self.Btn1.setEnabled(False)
        self.Btn1.clicked.connect( lambda: self.whichbtn(self.Btn1))
        self.Btn1.setGeometry(QtCore.QRect(60, 160, 121, 41))
        self.Btn1.setObjectName("Btn1")
        #Кнопка 1
        self.checkBox1 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox1.setGeometry(QtCore.QRect(70, 70, 101, 31))
        self.checkBox1.setObjectName("checkBox1")
        self.checkBox1.stateChanged.connect( self.btnstate)
        #Кнопка 2
        self.checkBox2 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox2.setGeometry(QtCore.QRect(70, 110, 101, 31))
        self.checkBox2.setObjectName("checkBox2")
        self.checkBox2.stateChanged.connect( self.btnstate)
        window.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(window)
        self.statusbar.setObjectName("statusbar")
        window.setStatusBar(self.statusbar)

        self.retranslateUi(window)
        QtCore.QMetaObject.connectSlotsByName(window)
        
    def closeEvent(self, event):
        close = QMessageBox()
        close.setText("Вы уверены что хотите выйти из программы?")
        close.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
        close.setWindowTitle("Завершение программы")
        close = close.exec()

        if close == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
            self.setWindowTitle("Сборник программ офицера ОБИ")
  
    def retranslateUi(self, window):
        _translate = QtCore.QCoreApplication.translate
        window.setWindowTitle(_translate("MainWindow", "Сборник программ офицера ОБИ"))
        self.Btn1.setText(_translate("MainWindow", "Установить"))
        self.checkBox1.setText(_translate("MainWindow", "Калькулятор"))
        self.checkBox2.setText(_translate("MainWindow", "Paint"))
        
    def btnstate(self):
        if self.checkBox1.isChecked():
            self.Btn1.setEnabled(True)
            print("Выбран элемент для установки - Калькулятор")
        if self.checkBox2.isChecked():
            self.Btn1.setEnabled(True)
            print("Выбран элемент для установки - Paint")

    def start_in_thread(self, program, data):
        Popen(program)
        self.progressBar.setValue(data)
        #time.sleep(10) # это для имитации долгой работы программы
        print(f'Запуск программы {program} завершен')
        
    startlist = []
    def whichbtn(self, w2):
        w2 = window_2()
        w2.setupUi()
        if self.checkBox1.isChecked():
            startlist = subprocess.Popen("C:/Windows/system32/calc.exe".split(),
                             shell=False,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
            self.stdout, self.stderr = startlist.communicate() #возврат
            #progPercent += 30
            
        if self.checkBox2.isChecked():
            startlist = subprocess.Popen("C:/Windows/system32/mspaint.exe".split(),
                             shell=False,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
            self.stdout, self.stderr = startlist.communicate()
            #progPercent += 30
        for program in startlist:
            t = threading.Thread(target=self.start_in_thread, args=[program])
            t.start() # запуск программы в отдельном потоке
            #self.updated.emit(int(self.progPercent))
            #self.tmr.updated.connect(progress2(Program))
            #self.tmr.start()
            window.exec()
            t.join() # ожидание окончания потока


main = QApplication(sys.argv)
window = window()
window.setupUi()
window.show()
sys.exit(main.exec_())
 

Вложения

  • Безымянный.jpg
    Безымянный.jpg
    131,1 КБ · Просмотры: 4

Jerry

Пользователь
Пользователь
Июл 28, 2020
29
12
3
Попробуйте для запуска внешних приложений использовать QProcess.
Примеры реализации есть как в документации, так и здесь и здесь.

Upd.
Видимо приведенный код сильно измененный вариант этого листинга.
Можно попробовать вернуть функцию def whichbtn(self, Btn): к исходному варианту. В текущем виде, помимо прочего, Popen срабатывает дважды на каждую программу.
 
Последнее редактирование:
  • Мне нравится
Реакции: Student и e8s22

e8s22

Новичок
Пользователь
Июн 24, 2020
7
0
1
Он мне не совсем подходил в плане выполнения процессов (фризится выполнение первой Popen на какое-то время, запускается другой, а время установки каждого приложения по моей задумке будет разным).В итоге получается что не завершится первый, появляется второй запуск. Сабпроцесс решил мою проблему (кстати я только сейчас проверил и понял, что он работает и без использования"for").

UPD: Используя старый вариант дочернее окно действительно перестало фризится, но оно пустое, без ProgressBar...
 
Последнее редактирование:

Jerry

Пользователь
Пользователь
Июл 28, 2020
29
12
3
Он мне не совсем подходил в плане выполнения процессов (фризится выполнение первой Popen на какое-то время, запускается другой, а время установки каждого приложения по моей задумке будет разным).

QProcess умеет дожидаться завершения работы процесса через waitForFinished (как минимум).

работает и без использования"for"

В текущем варианте (первый пост) есть логические ошибки (отметил в комментариях):
Python:
def start_in_thread(self, program, data):
    Popen(program)
    self.progressBar.setValue(data)
    print(f'Запуск программы {program} завершен')
     
startlist = [] # это должно быть в теле функции
def whichbtn(self, w2):
    w2 = window_2() # эта строка затирает входной параметр w2
    w2.setupUi()
    if self.checkBox1.isChecked():
        # следующая строка переопределяет переменную startlist
        startlist = subprocess.Popen("C:/Windows/system32/calc.exe".split(),
                          shell=False,
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE)
        self.stdout, self.stderr = startlist.communicate()
     
    if self.checkBox2.isChecked():
        # снова переопределение startlist
        startlist = subprocess.Popen("C:/Windows/system32/mspaint.exe".split(),
                          shell=False,
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE)
        self.stdout, self.stderr = startlist.communicate()

    # здесь starlist уже не список, если хотя бы один Popen() был вызван, цикл не отработает
    for program in startlist:
        t = threading.Thread(target=self.start_in_thread, args=[program])
        t.start()
        window.exec() # здесь, вероятно, имелось в виду w2.exec()?
        # хотя в документации ни QMainWindow ни QWidget метода exec() не имеют
        t.join()

В изначальном листинге Popen располагался в методе start_in_thread. Т.е. создавался список того, что нужно запустить, далее каждая программа запускалась отдельным потоком, в котором можно было выставить time.sleep() для имитации долгой работы.
В вашем коде Popen вызывается сразу, переопределяет переменную startlist, которая должна быть списком и далее итерируется по этому startlist, который в себе содержит уже экземпляр класса Popen. Поэтому цикл не работает, но даже если бы работал, то вызывал бы Popen еще раз, когда доходил до start_in_thread.
 
Последнее редактирование:
  • Мне нравится
Реакции: Student и e8s22

e8s22

Новичок
Пользователь
Июн 24, 2020
7
0
1
Вообщем, мне всё равно видимо нужна помощь, вот моя попытка использовать QProcess. По отдельности запуск работает, при выборе двух CheckBox не работает. И вторая проблема с запуском дочернего окна так и осталась у меня.

UPD: также я не понял какую роль выполняет partial, и правильно я понял, что цикл for в моём случае не нужен?
Код:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QProcess
from PyQt5.QtCore import QObject
from PyQt5.QtWidgets import QWidget, QDialog, QAction, QProgressBar, QMessageBox, QWidget, QMainWindow, QApplication
from subprocess import Popen
import threading
import time
import sys

class window_2(QtCore.QObject):
    resultsChanged = QtCore.pyqtSignal(list)
    def __init__(self):
        QtCore.QObject.__init__(self, parent)
        self.results = []
        self.m_processes = []
        self.number_process_running = 0
        super().__init__()
    def setupUi(self):
        self.w2 = window_2()
        self.w2.resize(250, 300) #работает
        self.w2.setWindowTitle('Прогресс бар')
        self.w2.progressBar = QProgressBar(self)
        self.w2.progressBar.setGeometry(0, 0, 300, 25)
        self.w2.progressBar.setProperty("value", 0)
        self.w2.progressBar.setMaximum(100)
        self.w2.show()
        self.step = 0
class window(QMainWindow):
    def __init__(self):
        super().__init__()
    def setupUi(self):
        window.resize(330, 259)
        quit = QAction("Quit", None )
        quit.triggered.connect(self.closeEvent)
        self.centralwidget = QtWidgets.QWidget(window)
        self.centralwidget.setObjectName("centralwidget")
        #Кнопка Установить
        self.Btn1 = QtWidgets.QPushButton(self.centralwidget)
        self.Btn1.setCheckable(False)
        self.Btn1.toggle()
        self.Btn1.setEnabled(False)
        self.Btn1.clicked.connect(self.zapusk)
        self.Btn1.setGeometry(QtCore.QRect(60, 160, 121, 41))
        self.Btn1.setObjectName("Btn1")
        #Кнопка 1
        self.checkBox1 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox1.setGeometry(QtCore.QRect(70, 70, 101, 31))
        self.checkBox1.setObjectName("checkBox1")
        self.checkBox1.stateChanged.connect( self.btnstate)
        #Кнопка 2
        self.checkBox2 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox2.setGeometry(QtCore.QRect(70, 110, 101, 31))
        self.checkBox2.setObjectName("checkBox2")
        self.checkBox2.stateChanged.connect( self.btnstate)
        window.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(window)
        self.statusbar.setObjectName("statusbar")
        window.setStatusBar(self.statusbar)

        self.retranslateUi(window)
        QtCore.QMetaObject.connectSlotsByName(window)
     
    def closeEvent(self, event):
        close = QMessageBox()
        close.setText("Вы уверены что хотите выйти из программы?")
        close.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
        close.setWindowTitle("Завершение программы")
        close = close.exec()

        if close == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
            self.setWindowTitle("Сборник программ офицера ОБИ")

    def retranslateUi(self, window):
        _translate = QtCore.QCoreApplication.translate
        window.setWindowTitle(_translate("MainWindow", "Сборник программ офицера ОБИ"))
        self.Btn1.setText(_translate("MainWindow", "Установить"))
        self.checkBox1.setText(_translate("MainWindow", "Калькулятор"))
        self.checkBox2.setText(_translate("MainWindow", "Paint"))
     
    def btnstate(self):
        if self.checkBox1.isChecked():
            self.Btn1.setEnabled(True)
            print("Выбран элемент для установки - Калькулятор")
        if self.checkBox2.isChecked():
            self.Btn1.setEnabled(True)
            print("Выбран элемент для установки - Paint")
         
               
    def zapusk(self, program):
        startlist = []
        process = QtCore.QProcess(self)
        if self.checkBox1.isChecked():
            startlist = process.start("C:/Windows/System32/mspaint.exe");
            startlist.number_process_running += 1
            startlist.results.append("")
            startlist.waitForFinished(-1);
            startlist.close();
        if self.checkBox2.isChecked():
            startlist = process.start("C:/Windows/System32/calc.exe");
            startlist.number_process_running += 1
            startlist.results.append("")
            startlist.waitForFinished(-1);
            startlist.close();
        else:
            startlist.readyReadStandardOutput.connect(partial(self.onReadyReadStandardOutput, programm))
           
    def onReadyReadStandardOutput(self, programm):
        process = self.sender()
        self.results[programm] = startlist.readAllStandardOutput()
        self.number_process_running -= 1
        if self.number_process_running <= 0:
            self.resultsChanged.emit(self.results)

main = QApplication(sys.argv)
window = window()
window.setupUi()
window.show()
sys.exit(main.exec_())
 
Последнее редактирование:

Форум IT Специалистов