2016-04-06 25 views
5

Tôi hiện đang trong quá trình tạo lớp tạo ra pyqtSignal (int) và pyqtSlot (int). Khó khăn nằm trong việc tạo ra một tín hiệu phát ra một giá trị cụ thể.PyQt5 trang trí khe cắm tín hiệu ví dụ

Giả sử tôi muốn tạo ra một cái gì đó tương tự như ví dụ đơn giản sau:

import sys 
from PyQt5.QtCore import (Qt, pyqtSignal, pyqtSlot) 
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider, 
    QVBoxLayout, QApplication) 


class Example(QWidget): 

    def __init__(self): 
     super().__init__() 

     self.initUI() 

    def printLabel(self, str): 
     print(str) 

    @pyqtSlot(int) 
    def on_sld_valueChanged(self, value): 
     self.lcd.display(value) 
     self.printLabel(value) 

    def initUI(self): 

     self.lcd = QLCDNumber(self) 
     self.sld = QSlider(Qt.Horizontal, self) 

     vbox = QVBoxLayout() 
     vbox.addWidget(self.lcd) 
     vbox.addWidget(self.sld) 

     self.setLayout(vbox) 
     self.sld.valueChanged.connect(self.on_sld_valueChanged) 

     self.setGeometry(300, 300, 250, 150) 
     self.setWindowTitle('Signal & slot') 
     self.show() 

if __name__ == '__main__': 

    app = QApplication(sys.argv) 
    ex = Example() 
    sys.exit(app.exec_()) 

câu hỏi đầu tiên của tôi cho đoạn code trên là:

  • Tại sao sử dụng() trang trí pyqtSlot ở tất cả khi loại bỏ pyqtSlot(int) không ảnh hưởng đến mã?
  • Bạn có thể đưa ra ví dụ về thời điểm cần thiết không?

Vì lý do cụ thể, tôi muốn tạo tín hiệu của riêng mình bằng cách sử dụng nhà máy pyqtSignal() và được cung cấp tài liệu phong nha here. Tuy nhiên, vấn đề duy nhất là, rất simple example không cung cấp một nền tảng vững chắc về cách phát ra các tín hiệu cụ thể.

Đây là những gì tôi đang cố gắng để làm nhưng đã thấy mình mất:

  1. Tạo một metaclass cho phép thực hiện nhiều loại khác nhau của các lớp con QWidget.
  2. Có metaclass sản xuất tín hiệu riêng của mình có thể được gọi từ bên ngoài lớp học.

Đây là những gì tôi sẽ cho:

from PyQt5.QtWidgets import QPushButton, QWidget 
from PyQt5.QtCore import pyqtSignal, pyqtSlot 
from PyQt5.QtWidgets import QSlider 

def template(Q_Type, name: str, *args): 
    class MyWidget(Q_Type): 

     def __init__(self) -> None: 
      super().__init__(*args) 
      self._name = name 

     def setSignal(self,type): 
      self.signal = pyqtSignal(type) 

     def callSignal(self): 
      pass 

    return MyWidget 

Như bạn có thể nhìn thấy. Tôi cung cấp cho các widget một tên bởi vì tôi thấy điều này là hữu ích, và tôi cũng cố gắng để nhanh chóng QWidget từ bên trong lớp để đơn giản hóa mã.

Đây là cách tôi muốn có một lớp học chính để sản xuất các vật dụng từ ví dụ đầu tiên:

import sys 
from PyQt5.QtCore import (Qt, pyqtSignal, pyqtSlot) 
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider, 
    QVBoxLayout, QApplication) 


class Example(QWidget): 

    def __init__(self): 
     super().__init__() 

     self.initUI() 

    def printLabel(self, str): 
     print(str) 

    @pyqtSlot(int) 
    def sld_valChanged(self, value): 
     self.lcd.display(value) 
     self.printLabel(value) 

    def initUI(self): 

     #instantiate the QWidgets through template class 
     self.lcd = template(QLCDNumber,'lcd_display') 
     self.sld = template(QSlider, 'slider', Qt.Horizontal) 

     #create signal 
     #self.sld.setSignal(int) 

     vbox = QVBoxLayout() 
     vbox.addWidget(self.lcd) 
     vbox.addWidget(self.sld) 

     self.setLayout(vbox) 

     #connect signal - this won't send the value of the slider 
     #self.sld.signal.connect(self.sld_valChanged) 
     self.sld.valueChanged.connect(self.sld_valChanged) 

     self.setGeometry(300, 300, 250, 150) 
     self.setWindowTitle('Signal & slot') 
     self.show() 

if __name__ == '__main__': 

    app = QApplication(sys.argv) 
    ex = Example() 
    sys.exit(app.exec_()) 

Chỉ có 2 vấn đề cần được giải quyết ở đây:

  1. Mẫu metaclass được thiết lập không chính xác và tôi không thể khởi tạo QWidget từ trong metaclass. Q_Type cho tôi biết loại của nó là PyQt5.QtCore.pyqtWrapperType khi cần là PyQt5.QtWidgets.QSlider.
  2. Mặc dù tôi đang tạo mộtSignal mới thông qua lớp templated, làm thế nào để tôi nhận được QSlider để gửi giá trị đã thay đổi mà tôi muốn nó gửi. Tôi tin rằng đây là nơi mà emit() đi vào chơi nhưng không có đủ kiến ​​thức về cách nó hữu ích. Tôi biết tôi có thể thực hiện một số chức năng gọi self.sld.emit(35) nếu tôi muốn tín hiệu chuyển giá trị 35 cho chức năng khe. Câu hỏi đặt ra không phải là cách mà tôi nên thực hiện chức năng này ở đâu?

Tôi có thể hoàn toàn mất cơ sở và đánh giá cao giải pháp, vui lòng sửa mã của tôi để tôi có thể có tín hiệu phát ra giá trị của thanh trượt.

+1

Tại sao bạn cố gắng viết mã xấu, quá phức tạp và khó bảo trì này? Có vấn đề thực sự nào mà bạn đang cố giải quyết không? Tôi có một linh cảm bạn đang làm cho sai lầm tân binh rất phổ biến của cố gắng để buộc các API để làm việc theo cách bạn (sai) nghĩ rằng họ * nên * làm việc, chứ không phải là cách họ đang thiết kế để làm việc. Đây là một biến thể của [XY Problem] (http://xyproblem.info/). – ekhumoro

+1

Việc sử dụng @pyqtSignal() không được chấp nhận. Chỉ sử dụng @pyqtSlot(). Kiểm tra tài liệu hướng dẫn cách sử dụng nó theo đúng cách. Đừng sáng tạo lại bánh xe. (Nếu bạn hỏi tôi, @pyqtSlot() nên được đặt tên là @pyQtSlot() để phù hợp với lược đồ đặt tên của PyQt.) –

Trả lời

3

Tại sao sử dụng trang trí pyqtSlot() ở tất cả khi loại bỏ pyqtSlot (int) không ảnh hưởng đến mã? Bạn có thể đưa ra ví dụ về khi nào cần không?

Đã được giải quyết trong số previous question của bạn, nhưng tôi sẽ lặp lại lần nữa.

Mặc dù PyQt4 cho phép bất kỳ Python callable được sử dụng như một khe khi tín hiệu kết nối, nó là đôi khi cần thiết để đánh dấu một cách rõ ràng phương pháp Python như là một khe Qt và để cung cấp một ++ chữ ký C cho nó. PyQt4 cung cấp trang trí hàm pyqtSlot() để làm điều này.

Kết nối tín hiệu với phương thức Python được trang trí cũng có lợi thế để giảm lượng bộ nhớ được sử dụng và hơi nhanh hơn .

Ngoài ra, như đề cập trong previous question của bạn, bạn không thể tạo tín hiệu như các biến Ví dụ, họ phải có thuộc tính lớp.

này sẽ không bao giờ làm việc

def setSignal(self,type): 
    self.signal = pyqtSignal(type) 

Nó phải được

class MyWidget(QWidget): 
    signal = pyqtSignal(int) 

Tạo một metaclass cho phép thực hiện nhiều loại khác nhau của các lớp con QWidget.

Bạn không thể xác định metaclass cho QObjects, vì chúng đã sử dụng metaclass của riêng mình (đây là điều cho phép ma thuật tín hiệu hoạt động). Tuy nhiên, bạn vẫn có thể tạo một nhà máy phân lớp QObject bằng chức năng type.

def factory(QClass, cls_name, signal_type): 
    return type(cls_name, (QClass,), {'signal': pyqtSignal(signal_type)}) 

MySlider = factory(QSlider, 'MySlider', int) 
self.sld = MySlider(Qt.Horizontal) 
self.sld.signal.connect(self.on_signal) 

Có metaclass tạo tín hiệu riêng của mình mà có thể được gọi từ bên ngoài lớp học.

Tóm lại, đừng làm điều này. Bạn không nên phát ra tín hiệu từ bên ngoài lớp học. Ví dụ về nhà máy ở trên không hữu ích lắm, bởi vì bạn không định nghĩa các phương thức tùy chỉnh trên các lớp đó để phát ra các tín hiệu đó. Thông thường, đây là cách bạn sẽ sử dụng các tín hiệu tùy chỉnh

class MyWidget(QWidget): 
    colorChanged = pyqtSignal() 

    def setColor(self, color): 
     self._color = color 
     self.colorChanged.emit() 


class ParentWidget(QWidget): 

    def __init__(self): 
     ... 
     self.my_widget = MyWidget(self) 
     self.my_widget.colorChanged.connect(self.on_colorChanged) 
     # This will cause the colorChanged signal to be emitted, calling on_colorChanged 
     self.my_widget.setColor(Qt.blue) 

    def on_colorChanged(self): 
     # do stuff 
+0

Câu hỏi của tôi sau đó dẫn đến: cách sắp xếp mã, đặc biệt là khi giao dịch với nhiều tiện ích. Nó có vẻ cẩu thả chỉ đơn giản là đặt vô số các vật dụng trong 1 lớp chính. – Max

+0

Một ứng dụng điển hình sẽ có hàng tá tiện ích, tất cả được chứa trong một cửa sổ hoặc hộp thoại. Không có gì cẩu thả về điều đó. Hãy xem các ứng dụng ví dụ Qt và PySide chính thức. Họ sẽ cung cấp cho bạn một ý tưởng về cách thức tổ chức các ứng dụng. –

+0

Giả sử tôi muốn ghi nhật ký thay đổi màu vào một tệp. (Những gì tôi muốn làm là đăng nhập mọi tín hiệu cho mọi widget và gọi một khung bên ngoài). Do đó tôi biết tôi có thể gọi hàm logging trong 'setColor' nhưng để gọi một hàm từ khung khác, tôi cần phải có' colorChanged' chuyển 'color' vào' on_colorChanged'. – Max

Các vấn đề liên quan