Уведомления

Группа в Telegram: @pythonsu

#1 Май 4, 2016 09:27:01

krdp
Зарегистрирован: 2015-02-25
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Не завершается поток

Доброго времени суток!

Для работы решил написать программу установки основных приложений для клиентов.
Въехал в следующий пень: функцию скачивания и запуска скачанного файла (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)

Офлайн

#2 Май 5, 2016 19:01:53

Kir@
Зарегистрирован: 2015-08-13
Сообщения: 124
Репутация: +  0  -
Профиль   Отправить e-mail  

Не завершается поток

Конвеер?
шикарный говнокод

Отредактировано Kir@ (Май 5, 2016 19:02:30)

Офлайн

#3 Май 7, 2016 18:31:16

krdp
Зарегистрирован: 2015-02-25
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Не завершается поток

Kir@
благодарю, но в моём случае поможет.. либо я не догоняю, как это применить в своём коде..

Офлайн

#4 Май 8, 2016 09:16:52

Kir@
Зарегистрирован: 2015-08-13
Сообщения: 124
Репутация: +  0  -
Профиль   Отправить e-mail  

Не завершается поток

Вызов следующей функции производить из предыдущей.

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)

Офлайн

#5 Май 8, 2016 12:05:58

krdp
Зарегистрирован: 2015-02-25
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Не завершается поток

Kir@
еще раз благодарю, но сами функции отрабатывают корректно. мой затык в том, что когда функция self.dload() вызывается как отдельный поток, то тогда либо вся прога крашится, либо не возвращается основной поток, т.е. функция self.startInstall() не продолжает выполняться..

Офлайн

#6 Май 8, 2016 13:39:06

Lestoroer
Зарегистрирован: 2015-12-24
Сообщения: 88
Репутация: +  5  -
Профиль   Отправить e-mail  

Не завершается поток

Ну, разобраться в этом коде (для меня) практически невозможно, но может вам помогут некоторые рекомендации:
1) Создайте отдельный класс, который будет унаследован от класса QThread.
2) В этом новом классе создайте метод, который будет исполняться отдельно от всего приложения.
3) В основном классе(где у вас все исполняется, я так понял это class Dialog) создайте объекта класса созданного в пункте 1.
4) Вызовите метод, который должен как раз-таки исполняться в отдельном потоке от класса созданного в пункте 3.

Отредактировано Lestoroer (Май 8, 2016 13:40:47)

Офлайн

#7 Май 12, 2016 07:27:13

krdp
Зарегистрирован: 2015-02-25
Сообщения: 11
Репутация: +  0  -
Профиль   Отправить e-mail  

Не завершается поток

Lestoroer
Благодарю за совет!
Нашел-таки время ознакомиться с QThread - отличная вещь!
Почти закончил программу! Вам “+”

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version