Bạn cần cho phép các sự kiện được xử lý trong khi vòng lặp đang chạy để ứng dụng có thể vẫn đáp ứng.
Quan trọng hơn, đối với các tác vụ dài hạn, bạn cần cung cấp cách để người dùng ngừng vòng lặp khi nó bắt đầu.
Một cách đơn giản để thực hiện việc này là bắt đầu vòng lặp với hẹn giờ, sau đó gọi định kỳ qApp.processEvents trong khi vòng lặp đang chạy.
Dưới đây là một kịch bản demo mà không rằng:
import sys, time
from PyQt4 import QtGui, QtCore
class ProgressBar(QtGui.QWidget):
def __init__(self, parent=None, total=20):
super(ProgressBar, self).__init__(parent)
self.progressbar = QtGui.QProgressBar()
self.progressbar.setMinimum(1)
self.progressbar.setMaximum(total)
self.button = QtGui.QPushButton('Start')
self.button.clicked.connect(self.handleButton)
main_layout = QtGui.QGridLayout()
main_layout.addWidget(self.button, 0, 0)
main_layout.addWidget(self.progressbar, 0, 1)
self.setLayout(main_layout)
self.setWindowTitle('Progress')
self._active = False
def handleButton(self):
if not self._active:
self._active = True
self.button.setText('Stop')
if self.progressbar.value() == self.progressbar.maximum():
self.progressbar.reset()
QtCore.QTimer.singleShot(0, self.startLoop)
else:
self._active = False
def closeEvent(self, event):
self._active = False
def startLoop(self):
while True:
time.sleep(0.05)
value = self.progressbar.value() + 1
self.progressbar.setValue(value)
QtGui.qApp.processEvents()
if (not self._active or
value >= self.progressbar.maximum()):
break
self.button.setText('Start')
self._active = False
app = QtGui.QApplication(sys.argv)
bar = ProgressBar(total=101)
bar.show()
sys.exit(app.exec_())
CẬP NHẬT
Giả sử rằng bạn đang sử dụng việc thực hiện C của python (tức CPython), giải pháp cho vấn đề này phụ thuộc hoàn toàn về bản chất của (các) nhiệm vụ phải chạy đồng thời với GUI. Về cơ bản hơn, nó được xác định bởi CPython có Global Interpreter Lock (GIL).
Tôi sẽ không thử bất kỳ giải thích nào về GIL của CPython: thay vào đó, tôi sẽ chỉ đơn giản đề nghị xem này tuyệt vời PyCon video bởi Dave Beazley, và để nó ở đó.
Nói chung, khi cố gắng chạy GUI đồng thời với tác vụ nền, câu hỏi đầu tiên cần đặt ra là: Tác vụ IO bị ràng buộc hoặc CPU bị ràng buộc?
Nếu nó bị ràng buộc IO (ví dụ: truy cập hệ thống tệp cục bộ, tải xuống từ internet, v.v.), thì giải pháp thường khá đơn giản, vì CPython luôn phát hành GIL cho các hoạt động I/O. Nhiệm vụ nền đơn giản có thể được thực hiện asynchronously hoặc được thực hiện bởi một worker thread và không cần phải thực hiện những gì đặc biệt để giữ cho giao diện GUI được đáp ứng.
Những khó khăn chính xảy ra với các tác vụ bị ràng buộc CPU, khi có câu hỏi thứ hai để hỏi: Tác vụ có thể được chia nhỏ thành một loạt các bước nhỏ không?
Nếu có thể, giải pháp là gửi định kỳ các yêu cầu tới luồng GUI để xử lý các sự kiện đang chờ xử lý hiện tại của nó. Kịch bản demo ở trên là một ví dụ thô của kỹ thuật này. Thông thường, nhiệm vụ sẽ được thực hiện trong một chuỗi công nhân riêng biệt, mà sẽ phát ra tín hiệu gui-update khi mỗi bước của nhiệm vụ được hoàn thành. (NB: điều quan trọng là đảm bảo rằng chuỗi công nhân không bao giờ cố gắng thực hiện bất kỳ hoạt động liên quan đến GUI nào).
Nhưng nếu nhiệm vụ không thể được chia thành các bước nhỏ, thì không có giải pháp kiểu luồng thông thường nào hoạt động. GUI sẽ chỉ đóng băng cho đến khi tác vụ được hoàn thành, cho dù chủ đề có được sử dụng hay không.
Đối với kịch bản cuối cùng này, giải pháp duy nhất là sử dụng quy trình riêng biệt, thay vì một chuỗi riêng biệt - tức là sử dụng mô-đun multiprocessing. Tất nhiên, giải pháp này sẽ chỉ có hiệu quả nếu hệ thống đích có nhiều lõi CPU có sẵn. Nếu chỉ có một lõi CPU để chơi, thì về cơ bản không có gì có thể được thực hiện để trợ giúp (ngoài việc chuyển sang thực thi khác nhau của Python, hoặc bằng một ngôn ngữ khác hoàn toàn).
Nếu tôi chạy cửa sổ GUI này cũng như với lỗi "Không phản hồi, buộc đóng". Tuy nhiên, nếu tôi đợi cho đến khi tất cả các nhiệm vụ của tôi kết thúc thì ứng dụng bình thường vẫn tiếp tục. – Tuim
@Tuim. Kịch bản của tôi chỉ là một bản demo đơn giản dựa trên mã trong câu hỏi của bạn. Nó không phải là một giải pháp phổ quát mà sẽ làm việc trong mọi tình huống. Bạn cần phải cập nhật câu hỏi của bạn với một lời giải thích thích hợp về những gì bạn đang cố gắng làm. "Nhiệm vụ" bạn đề cập đến là gì? Họ có bị ràng buộc CPU hay bị ràng buộc bởi IO không? Ứng dụng có thực hiện nhiệm vụ mà bạn đã tự viết và có thể sửa đổi không? Nó được viết bằng ngôn ngữ nào? Vv, v.v. – ekhumoro
Các tác vụ này là các cài đặt, ví dụ: giải nén các tệp zip, cài đặt các gói msi/deb như thế. Nhưng điều này không liên quan lắm đến vụ việc. Ứng dụng này được viết bằng Python và có thể tùy chỉnh hoàn toàn. Ngoài ra tôi không mong đợi một câu trả lời có thể sao chép-dán! Tôi đang mong đợi một gợi ý đúng hướng và cái mà bạn có dường như không đúng hướng cho tôi, tôi đã thử. Không có hành vi phạm tội. – Tuim