Форум сайта python.su
0
Доброго времени суток!
Для работы решил написать программу установки основных приложений для клиентов.
Въехал в следующий пень: функцию скачивания и запуска скачанного файла (self.dload()) запускаю в отдельном потоке, чтобы GUI не вис, однако поток не завершается и программа либо вся падает, либо не продолжает выполнение основного сценария после установки первого скачанного файла.
Прилагаю упрощенный код. Что делаю не так? Поделитесь с новичком опытом, пожалуйста)
# -*- coding: utf-8 -*- from PyQt4 import QtCore, QtGui from PyQt4.QtCore import * from PyQt4.QtGui import * import os, subprocess, urllib from threading import Thread from datetime import datetime, date, time title = u'Installer' url = '' class Dialog(QtGui.QMainWindow): def __init__(self, parent=None): super(Dialog, self).__init__(parent) self.createMenu() self.createSelectActionBox() self.createProgramListBox() self.createAboutBox() # Создание и размещение элементов окна self.mainWidget = QtGui.QWidget() mainLayout = QtGui.QVBoxLayout() mainLayout.setMenuBar(self.menuBar) mainLayout.addWidget(self.programListBox) mainLayout.addWidget(self.selectActionBox) mainLayout.addWidget(self.formAboutBox) self.mainWidget.setLayout(mainLayout) self.centralWidget = QtGui.QWidget() self.centralWidget.setLayout(mainLayout) self.setCentralWidget(self.centralWidget) self.setMinimumSize(1024, 628) self.setWindowState(QtCore.Qt.WindowMaximized) self.setWindowTitle(title) screen = QtGui.QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2) self.statusBar().show() self.progresslabel = QtGui.QLabel(self.statusBar()) self.progresslabel.hide() self.progressbar = QProgressBar(self.statusBar()) self.progressbar.hide() self.dl_break_btn = QtGui.QPushButton(u'Отмена') self.dl_break_btn.hide() self.statusBar().addPermanentWidget(self.progresslabel) self.statusBar().addPermanentWidget(self.progressbar) self.statusBar().addPermanentWidget(self.dl_break_btn) # Панель меню def createMenu(self): self.menuBar = QtGui.QMenuBar() self.fileMenu = QtGui.QMenu(u'&Файл ', self) self.exitAction = self.fileMenu.addAction(u'В&ыход') self.menuBar.addMenu(self.fileMenu) # Список программ для установки def createProgramListBox(self): self.programListBox = QtGui.QGroupBox(u'Выберите программы для установки:') self.layoutt = QtGui.QGridLayout() self.scrollLayout = QtGui.QFormLayout self.scrollWidget = QtGui.QWidget() self.scrollWidget.setLayout(self.layoutt) self.scrollArea = QtGui.QScrollArea() self.scrollArea.setWidgetResizable(True) self.scrollArea.setWidget(self.scrollWidget) self.mainLayout = QtGui.QVBoxLayout() self.mainLayout.addWidget(self.scrollArea) #Драйверы self.cb_Drivers = QtGui.QCheckBox(u'# Драйверы', self) self.cb_Drivers.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(self.cb_Drivers, QtCore.SIGNAL('stateChanged(int)'), self.cb_DriversCheck) self.layoutt.addWidget(self.cb_Drivers, 6, 0) self.cb_Rutoken_40 = QtGui.QCheckBox('Rutoken 4.0', self) self.cb_Rutoken_40.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(self.cb_Rutoken_40, QtCore.SIGNAL('stateChanged(int)'), self.cb_Rutoken_40Check) self.cb_Etoken_x86_x64 = QtGui.QCheckBox('Etoken x86+x64', self) self.cb_Etoken_x86_x64.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(self.cb_Etoken_x86_x64, QtCore.SIGNAL('stateChanged(int)'), self.cb_Etoken_x86_x64Check) self.logField = QtGui.QTextEdit() self.logField.setFixedWidth(532) self.logField.setReadOnly(True) self.layoutt.addWidget(self.logField, 0, 2, 37, 1) self.layoutt.setColumnStretch(1, 10) self.layoutt.setColumnStretch(2, 20) self.programListBox.setLayout(self.mainLayout) #Отмена выбранного: def cbUncheck(self): #Drivers: if self.cb_Rutoken_40.isChecked: self.cb_Rutoken_40.setChecked(False) if self.cb_Etoken_x86_x64.isChecked: self.cb_Etoken_x86_x64.setChecked(False) # Панель кнопок def createSelectActionBox(self): self.selectActionBox = QtGui.QGroupBox(u'Выберите действие') layout = QtGui.QHBoxLayout() self.button_startInstall = QtGui.QPushButton(u'Начать установку', self) self.button_startInstall.clicked.connect(self.startInstall) layout.addWidget(self.button_startInstall) self.button_removeCheck = QtGui.QPushButton(u'Отмена выбранного', self) self.button_removeCheck.clicked.connect(self.cbUncheck) layout.addWidget(self.button_removeCheck) self.button_reboot = QtGui.QPushButton(u'Перезагрузить компьютер', self) self.button_reboot.clicked.connect(self.rebootEvent) layout.addWidget(self.button_reboot) self.button_exit = QtGui.QPushButton(u'Закрыть', self) self.button_exit.clicked.connect(self.closeEvent) layout.addWidget(self.button_exit) self.selectActionBox.setLayout(layout) # Функция установки выбранных программ def startInstall(self): global url softList = '' #Драйверы: if self.cb_Rutoken_40.isChecked(): softList += u'• Rutoken 4.0\n' else: softList[:14] if self.cb_Etoken_x86_x64.isChecked(): softList += u'• Etoken x86+x64\n' else: softList[:17] if softList == '': QtGui.QMessageBox.warning(self, u'Внимание!', u'Вы ничего не выбрали!', QtGui.QMessageBox.Ok) else: reply = QtGui.QMessageBox.information(self, u'Запуск установки', u'Назначена установка:\n\n' + softList + u'\nПродолжить?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: self.logField.append(u'\n%Поехали!' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') if self.cb_Rutoken_40.isChecked(): self.logField.append(u'\n... ☼ Скачивается <Rutoken 4.0> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') url = 'http://wiki.7405405.ru/images/rtDrivers.exe' t0 = Thread(target=self.dload) t0.start() t0.setDeamon(True) if self.cb_Etoken_x86_x64.isChecked(): self.logField.append(u'\n... ☼ Скачивается <Etoken_x86+x64> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') url = 'http://www.aladdin-rd.ru/upload/iblock/749/etokenpkiclient51sp1_2010-10-19.zip' t1 = Thread(target=self.dload) t1.start() t1.setDeamon(True) QtGui.QMessageBox.information(self, u'Внимание!', u'☺ Установка отмеченных приложений завершена', QtGui.QMessageBox.Ok) self.logField.append(u'\n☺ Установка отмеченных приложений завершена (' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')\n') #cb_Drivers: def cb_DriversCheck(self, value): if self.cb_Drivers.isChecked(): self.logField.append(u'• Открыт раздел <Драйверы> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') self.layoutt.addWidget(self.cb_Rutoken_40, 7, 1) self.cb_Rutoken_40.setVisible(True) self.layoutt.addWidget(self.cb_Etoken_x86_x64, 8, 1) self.cb_Etoken_x86_x64.setVisible(True) else: self.logField.append(u'(x) Закрыт раздел <Драйверы> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') self.layoutt.removeWidget(self.cb_Rutoken_40) self.cb_Rutoken_40.setVisible(False) self.layoutt.removeWidget(self.cb_Etoken_x86_x64) self.cb_Etoken_x86_x64.setVisible(False) #cb_Rutoken_40Check def cb_Rutoken_40Check(self): if self.cb_Rutoken_40.isChecked(): self.logField.append(u' (+) Назначена установка <Rutoken 4.0> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') else: self.logField.append(u' (-) Отмена установки <Rutoken 4.0> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') #cb_Etoken_x86_x64Check def cb_Etoken_x86_x64Check(self): if self.cb_Etoken_x86_x64.isChecked(): self.logField.append(u' (+) Назначена установка <Etoken x86+x64> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') else: self.logField.append(u' (-) Отмена установки <Etoken x86+x64> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') def createAboutBox(self): self.formAboutBox = QtGui.QGroupBox(u'О программе') layout = QtGui.QFormLayout() self.formAboutBox.setLayout(layout) # Функция скачивания и запуска скачанного файла def dload(self): try: response = os.system(r'ping -n 2 8.8.8.8') except: response = subprocess.Popen(['ping', '8.8.8.8', '-n', '2'], stdout=subprocess.PIPE).stdout.read() if response != 0: try: path = os.path.expanduser(os.path.join('~', unicode(url).split('/')[-1])) except: path = os.path.abspath(os.path.join('~', unicode(url).split('/')[-1])) destination = QtGui.QFileDialog.getSaveFileName(self, u"Сохранение...", path) if destination: u = urllib.urlopen(url) f = open(unicode(destination), 'wb') meta = u.info() file_size = int(meta.getheaders("Content-Length")[0]) file_size_dl = 0 block_sz = 8192 while True: buffer = u.read(block_sz) if not buffer: if file_size_dl < file_size: QtGui.QMessageBox.warning(self, u'Внимание!', u'Ошибка чтения данных!\n Повторите попытку.', QtGui.QMessageBox.Cancel) else: break file_size_dl += len(buffer) f.write(buffer) status = r"%10d байт [%3.2f%%]" % (file_size_dl, file_size_dl * 100 / file_size) self.setWindowTitle(title + u' - Скачивание в ' + path + ' ' + status) ## dialog.progresslabel.show() self.progresslabel.setText(u'Скачивание в ' + path + ' ') self.progressbar.setMinimum(1) self.progressbar.setMaximum(file_size) self.progressbar.setValue(file_size_dl) self.progressbar.show() self.dl_break_btn.show() f.flush() f.close() self.logField.append(u'\n>>> ☺ Скачивание завершено (' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') self.progresslabel.hide() self.progressbar.hide() self.dl_break_btn.hide() self.setWindowTitle(title) self.logField.append(u'\n... ☼ Установка... (' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') os.startfile(destination) reply = QtGui.QMessageBox.question(self, u'Внимание!', u'Сохранить файл дистрибутива?\n' u'(путь сохранения ' + unicode(destination) + ')', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.No: if os.path.exists(unicode(destination)): try: os.remove(unicode(destination)) QtGui.QMessageBox.information(self, u'Внимание!', u'Файл дистрибутива удалён', QtGui.QMessageBox.Ok) except: QtGui.QMessageBox.warning(self, u'Внимание!', u'Ошибка удаления файла дистрибутива.\n' + u'Возможно, недостаточно прав для данной операции\n' + u'или файл занят другим процессом.\n' + u'При необходимости удалите файл вручную.', QtGui.QMessageBox.Ok) else: QtGui.QMessageBox.warning(self, u'Внимание!', u'Файл дистрибутива не найден.', QtGui.QMessageBox.Ok) self.logField.append(u'>>> ☺ Установка завершена' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') else: QtGui.QMessageBox.warning(self, u'Внимание!', u'Отмена установки', QtGui.QMessageBox.Cancel) self.logField.append(u'\n>>> ☻ Отмена установки!\n') self.startItstall().stop() dialog.stopInstalling() else: QtGui.QMessageBox.critical(self, u'Внимание!', u'Проверьте настройки интернет-соединения!', QtGui.QMessageBox.Cancel) self.logField.append(u'\n{http://} Проверьте настройки интернет-соединения!\n') # Подтверждение продолжения установки def stopInstalling(self): repLy = QtGui.QMessageBox.question(self, u'Внимание!', u'Продолжить установку отмеченных программ?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if repLy == QtGui.QMessageBox.No: self.logField.append(u'\n>>> ☻ Дальнейшая установка отмеченных приложений прервана пользователем (' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') self.startItstall().stop() else: QtGui.QMessageBox.information(self, u'Внимание!', u'Едем дальше...', QtGui.QMessageBox.Ok) # Подтверждение перезагрузки компьютера: def rebootEvent(self, event): reply = QtGui.QMessageBox.question(self, u'Внимание!', u'Вы действительно желаете перезагрузить компьютер?\n' u'Несохранённые данные будут утеряны.\n' u'Продолжить?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: os.system('shutdown -r -f -t 3') sys.exit() else: event.ignore() # Подтверждение закрытия приложения: def closeEvent(self, event): reply = QtGui.QMessageBox.question(self, u'Внимание!', u"Вы действительно желаете закрыть приложение?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: sys.exit() else: event.ignore() if __name__ == '__main__': import sys reload(sys) sys.setdefaultencoding('utf-8') app = QtGui.QApplication(sys.argv) dialog = Dialog() dialog.show() sys.exit(app.exec_())
Отредактировано krdp (Май 4, 2016 10:38:26)
Офлайн
0
Конвеер?
шикарный говнокод 
Отредактировано Kir@ (Май 5, 2016 19:02:30)
Офлайн
0
Kir@
благодарю, но в моём случае поможет.. либо я не догоняю, как это применить в своём коде..
Офлайн
0
Вызов следующей функции производить из предыдущей.
def cb_Etoken_x86_x64Check(self): if self.cb_Etoken_x86_x64.isChecked(): self.logField.append(u' (+) Назначена установка <Etoken x86+x64> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') else: self.logField.append(u' (-) Отмена установки <Etoken x86+x64> ' + '(' + datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") + ')') createAboutBox() def createAboutBox(self): ...
Отредактировано Kir@ (Май 8, 2016 09:17:10)
Офлайн
0
Kir@
еще раз благодарю, но сами функции отрабатывают корректно. мой затык в том, что когда функция self.dload() вызывается как отдельный поток, то тогда либо вся прога крашится, либо не возвращается основной поток, т.е. функция self.startInstall() не продолжает выполняться..
Офлайн
5
Ну, разобраться в этом коде (для меня) практически невозможно, но может вам помогут некоторые рекомендации:
1) Создайте отдельный класс, который будет унаследован от класса QThread.
2) В этом новом классе создайте метод, который будет исполняться отдельно от всего приложения.
3) В основном классе(где у вас все исполняется, я так понял это class Dialog) создайте объекта класса созданного в пункте 1.
4) Вызовите метод, который должен как раз-таки исполняться в отдельном потоке от класса созданного в пункте 3.
Отредактировано Lestoroer (Май 8, 2016 13:40:47)
Офлайн
0
Lestoroer
Благодарю за совет!
Нашел-таки время ознакомиться с QThread - отличная вещь!
Почти закончил программу! Вам “+”
Офлайн