Saya mencoba memperbarui pyqt QProgressBar dari beberapa utas, dan dari apa yang saya pahami, cara terbaik untuk melakukannya adalah dengan memancarkan sinyal kembali ke utas GUI utama (saya mencoba meneruskan objek QProgressBar ke utas pekerja dan meskipun tampaknya untuk bekerja saya mendapat banyak peringatan di juru bahasa). Dalam kode berikut saya mengatur sinyal progressSignal dan menghubungkannya ke utas yang (untuk saat ini) hanya mencetak apa pun yang dipancarkan. Saya kemudian memancarkan dari setiap utas persentase total. Saya tahu ini bekerja di luar utas dengan hanya membuang pancaran acak di baris 47, yang memang masuk. Namun pancaran dari jalur 36 tidak memicu apa pun, jadi sepertinya tidak pernah berhasil...

import Queue, threading
from PyQt4 import QtCore
import shutil
import profile

fileQueue = Queue.Queue()

class Communicate(QtCore.QObject):

    progressSignal = QtCore.pyqtSignal(int)

class ThreadedCopy:
    totalFiles = 0
    copyCount = 0
    lock = threading.Lock()

    def __init__(self, inputList, progressBar="Undefined"):
        self.totalFiles = len(inputList)

        self.c = Communicate()
        self.c.progressSignal.connect(self.updateProgressBar)

        print str(self.totalFiles) + " files to copy."
        self.threadWorkerCopy(inputList)


    def CopyWorker(self):
        while True:
            self.c.progressSignal.emit(2000)
            fileName = fileQueue.get()
            shutil.copy(fileName[0], fileName[1])
            fileQueue.task_done()
            with self.lock:
                self.copyCount += 1
                percent = (self.copyCount * 100) / self.totalFiles
                self.c.progressSignal.emit(percent)

    def threadWorkerCopy(self, fileNameList):

        for i in range(16):
            t = threading.Thread(target=self.CopyWorker)
            t.daemon = True
            t.start()
        for fileName in fileNameList:
            fileQueue.put(fileName)
        fileQueue.join()
        self.c.progressSignal.emit(1000)

    def updateProgressBar(self, percent):
        print percent

MEMPERBARUI:

Inilah sampel dengan gui. Yang ini berjalan tetapi cukup tidak stabil, macet secara teratur dan UI melakukan beberapa hal aneh (bilah kemajuan tidak selesai, dll.)

Main.py:

import sys, os
import MultithreadedCopy_5
from PyQt4 import QtCore, QtGui

def grabFiles(path):
    # gets all files (not folders) in a directory
    for file in os.listdir(path):
        if os.path.isfile(os.path.join(path, file)):
            yield os.path.join(path, file)

class MainWin(QtGui.QWidget):

    def __init__(self):
        super(MainWin, self).__init__()
        self.initUI()

    def initUI(self):
        self.progress = QtGui.QProgressBar()

        box = QtGui.QVBoxLayout()
        box.addWidget(self.progress)
        goBtn = QtGui.QPushButton("Start copy")
        box.addWidget(goBtn)

        self.setLayout(box)

        goBtn.clicked.connect(self.startCopy)

    def startCopy(self):
        files = grabFiles("folder/with/files")
        fileList = []
        for file in files:
            fileList.append([file,"folder/to/copy/to"])

        MultithreadedCopy_5.ThreadedCopy(fileList, self.progress)

def main():
    app = QtGui.QApplication(sys.argv)
    ex = MainWin()
    ex.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

MultithreadedCopy_5.py:

import Queue, threading
from PyQt4 import QtCore
import shutil
import profile

fileQueue = Queue.Queue()

class Communicate(QtCore.QObject):

    progressSignal = QtCore.pyqtSignal(int)

class ThreadedCopy:
    totalFiles = 0
    copyCount = 0
    lock = threading.Lock()

    def __init__(self, inputList, progressBar="Undefined"):
        self.progressBar = progressBar
        self.totalFiles = len(inputList)

        self.c = Communicate()
        self.c.progressSignal.connect(self.updateProgressBar, QtCore.Qt.DirectConnection)

        print str(self.totalFiles) + " files to copy."
        self.threadWorkerCopy(inputList)


    def CopyWorker(self):
        while True:
            fileName = fileQueue.get()
            shutil.copy(fileName[0], fileName[1])
            fileQueue.task_done()
            with self.lock:
                self.copyCount += 1
                percent = (self.copyCount * 100) / self.totalFiles
                self.c.progressSignal.emit(percent)

    def threadWorkerCopy(self, fileNameList):
        for i in range(16):
            t = threading.Thread(target=self.CopyWorker)
            t.daemon = True
            t.start()
        for fileName in fileNameList:
            fileQueue.put(fileName)
        fileQueue.join()

    def updateProgressBar(self, percent):
        self.progressBar.setValue(percent)

#profile.run('ThreadedCopy()')
0
Spencer 8 Agustus 2017, 01:11

2 jawaban

Jawaban Terbaik

Masalah utama adalah penundaan waktu antara pengiriman sinyal dan penerimaan, kita dapat mengurangi waktu itu menggunakan processEvents():

Anda dapat memanggil fungsi ini sesekali ketika program Anda sedang sibuk melakukan operasi yang lama (misalnya menyalin file).

def CopyWorker(self):
    while True:
        fileName = fileQueue.get()
        shutil.copy(fileName[0], fileName[1])
        fileQueue.task_done()
        with self.lock:
            self.copyCount += 1
            print(self.copyCount)
            percent = (self.copyCount * 100) / self.totalFiles
            self.c.progressSignal.emit(percent)
            QtCore.QCoreApplication.processEvents()
-5
6 revs 5 Maret 2018, 05:49

Ada dua masalah utama dengan contoh Anda.

Pertama, objek yang memancarkan sinyal dibuat di utas utama/gui, sehingga sinyal apa pun yang dipancarkannya tidak akan menjadi utas silang, dan karenanya tidak aman untuk utas. Solusi yang jelas untuk ini adalah membuat objek pensinyalan di dalam fungsi target utas pekerja - yang berarti harus ada contoh terpisah untuk setiap utas.

Kedua, while-loop di dalam fungsi target tidak pernah dihentikan, yang berarti setiap objek ThreadedCopy akan tetap hidup setelah operasi penyalinan saat ini selesai. Karena semua objek ini berbagi antrian yang sama, perilaku akan menjadi tidak terduga jika ada upaya yang dilakukan untuk mengulangi operasi penyalinan. Solusi yang jelas untuk ini adalah keluar dari while-loop setelah antrian kosong.

Di bawah ini adalah penulisan ulang MultithreadedCopy_5.py yang seharusnya menyelesaikan masalah ini. Namun, seperti yang dinyatakan dalam komentar, saya masih sangat menyarankan menggunakan QThread daripada utas python dalam skenario ini, karena kemungkinan akan memberikan solusi yang jauh lebih kuat dan lebih mudah dipelihara.

import Queue, threading
from PyQt4 import QtCore
import shutil
import profile

fileQueue = Queue.Queue()

class Communicate(QtCore.QObject):
    progressSignal = QtCore.pyqtSignal(int)

class ThreadedCopy:
    totalFiles = 0
    copyCount = 0
    lock = threading.Lock()

    def __init__(self, inputList, progressBar="Undefined"):
        self.progressBar = progressBar
        self.totalFiles = len(inputList)
        print str(self.totalFiles) + " files to copy."
        self.threadWorkerCopy(inputList)

    def CopyWorker(self):
        c = Communicate()
        c.progressSignal.connect(self.updateProgressBar)
        while True:
            try:
                fileName = fileQueue.get(False)
            except Queue.Empty:
                break
            else:
                shutil.copy(fileName[0], fileName[1])
                with self.lock:
                    self.copyCount += 1
                    percent = (self.copyCount * 100) / self.totalFiles
                    c.progressSignal.emit(percent)
                fileQueue.task_done()

    def threadWorkerCopy(self, fileNameList):
        if fileQueue.empty():
            for i in range(16):
                t = threading.Thread(target=self.CopyWorker)
                t.daemon = True
                t.start()
            for fileName in fileNameList:
                fileQueue.put(fileName)
            fileQueue.join()

    def updateProgressBar(self, percent):
        self.progressBar.setValue(percent)
2
ekhumoro 10 Agustus 2017, 17:29