2012-11-07 32 views
10

Khi sử dụng mã sau đây, đơn đăng ký của tôi sẽ sau một vài giây. Và bởi các quầy hàng tôi có nghĩa là treo cứng. Tôi nhận được một cửa sổ từ Windows nói rằng chờ đợi hoặc lực lượng gần.Tiến trình QT PythonBar

Tôi có thể thêm rằng điều này chỉ xảy ra khi tôi nhấp vào bên trong cửa sổ thanh tiến trình hoặc khi tôi nhấp vào bên ngoài cửa sổ để nó mất tiêu điểm. Nếu tôi bắt đầu ví dụ và không chạm vào bất cứ điều gì nó hoạt động như nó nên.

from PyQt4 import QtCore 
from PyQt4 import QtGui 


class ProgressBar(QtGui.QWidget): 
    def __init__(self, parent=None, total=20): 
     super(ProgressBar, self).__init__(parent) 
     self.name_line = QtGui.QLineEdit() 

     self.progressbar = QtGui.QProgressBar() 
     self.progressbar.setMinimum(1) 
     self.progressbar.setMaximum(total) 

     main_layout = QtGui.QGridLayout() 
     main_layout.addWidget(self.progressbar, 0, 0) 

     self.setLayout(main_layout) 
     self.setWindowTitle("Progress") 

    def update_progressbar(self, val): 
     self.progressbar.setValue(val) 

Sử dụng này như sau:

app = QtGui.QApplication(sys.argv) 
bar = ProgressBar(total=101) 
bar.show() 

for i in range(2,100): 
    bar.update_progressbar(i) 
    time.sleep(1) 

Cảm ơn sự giúp đỡ nào.

Trả lời

12

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).

+0

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

+1

@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

+0

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

0

khi bạn lập trình gui, bạn cần sử dụng một dạng đa luồng, bạn sẽ có chuỗi công nhân và chuỗi cập nhật gui và trả lời các sự kiện chuột và bàn phím. Có một số cách để làm điều đó. Tôi khuyên bạn nên xem QProgressBar tutorial có một ví dụ làm việc tuyệt vời về cách sử dụng QProgressBar.

Trong hướng dẫn, họ đang sử dụng QBasicTimer là cách để kiểm soát trở lại luồng chính để nó có thể phản hồi các sự kiện GUI. Bằng cách sử dụng time.sleep trong mã của bạn, bạn đang chặn trong một giây luồng duy nhất đang chạy.

+0

Tôi biết điều đó. Nhưng họ không làm cho bất kỳ sử dụng nhiều luồng như tôi trong ví dụ. – Tuim

+1

@Tuim: Có, chúng không sử dụng 'time.sleep()'. – Junuxx

+0

@Tuim Tôi đã cập nhật câu trả lời để giải thích thêm tại sao mã của bạn và các hướng dẫn không làm điều tương tự. –