2012-01-11 31 views
7

Tôi có khoảng 10 QAction (số này sẽ khác nhau trong thời gian chạy) trên thanh công cụ, tất cả sẽ thực hiện tương tự nhưng sử dụng các tham số khác nhau. Tôi đang suy nghĩ để thêm tham số như một thuộc tính cho đối tượng QAction, và sau đó, tín hiệu kích hoạt của QAction cũng sẽ gửi chính đối tượng của nó tới hàm gọi lại, để tôi có thể nhận các tham số cần thiết cho hàm đó. Tôi thực sự có 2 câu hỏi về điều này:Làm thế nào để chuyển đối số đến hàm gọi lại trong PyQt

  • Có thể làm được không?
  • Có cách nào tốt hơn để thực hiện việc này không?

Trả lời

4

Bạn có thể tự gửi đối tượng hành động bằng cách sử dụng signal mapper. Tuy nhiên, nó có thể là tốt hơn để chỉ cần gửi một định danh và làm tất cả các công việc trong xử lý tín hiệu.

Dưới đây là một kịch bản giới thiệu đơn giản:

from PyQt4 import QtGui, QtCore 

class Window(QtGui.QMainWindow): 
    def __init__(self): 
     QtGui.QMainWindow.__init__(self) 
     self.mapper = QtCore.QSignalMapper(self) 
     self.toolbar = self.addToolBar('Foo') 
     self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) 
     for text in 'One Two Three'.split(): 
      action = QtGui.QAction(text, self) 
      self.mapper.setMapping(action, text) 
      action.triggered.connect(self.mapper.map) 
      self.toolbar.addAction(action) 
     self.mapper.mapped['QString'].connect(self.handleButton) 
     self.edit = QtGui.QLineEdit(self) 
     self.setCentralWidget(self.edit) 

    def handleButton(self, identifier): 
     if identifier == 'One': 
      text = 'Do This' 
     elif identifier == 'Two': 
      text = 'Do That' 
     elif identifier == 'Three': 
      text = 'Do Other' 
     self.edit.setText(text) 

if __name__ == '__main__': 

    import sys 
    app = QtGui.QApplication(sys.argv) 
    window = Window() 
    window.resize(300, 60) 
    window.show() 
    sys.exit(app.exec_()) 
+0

Câu trả lời này dường như không phổ biến lắm. 'QSignalMapper' có được sử dụng rộng rãi không? – 101

+0

@figs. Tôi không biết tại sao OP chấp nhận câu trả lời này, cho rằng giải pháp 'lambda/partial' thông thường đã được đưa ra. Đã có một lần khi 'QSignalMapper' hữu ích hơn, vì PyQt không phải lúc nào cũng được hỗ trợ bằng cách sử dụng' lambda/partial' như một khe - nhưng đó là một thời gian dài trước đây.Tại thời điểm này, mặc dù, tôi không thể nghĩ về một kịch bản mà nó có thể có hiệu quả hơn (khác hơn trong C + +, tất nhiên). – ekhumoro

35

Làm thế nào để vượt qua đối số cho hàm callback trong PyQt

Bạn có thể sử dụng functools.partial từ thư viện standart Python. Ví dụ với QAction:

some_action.triggered.connect(functools.partial(some_callback, param1, param2)) 
+1

Rất đơn giản và hiệu quả. IMHO, đây phải là câu trả lời được chấp nhận. – reima

+0

Tôi chỉ sử dụng giải pháp này. Hoạt động tốt! – swdev

1

Vẫn là một lỗi trong cách sử dụng PyQt QSignalMapper. Tôi phát hiện ra một công việc xung quanh đây:

http://www.riverbankcomputing.com/pipermail/pyqt/2010-March/026113.html

Để khắc phục các câu hỏi đã trả lời thực hiện thay đổi sau đây:

 action.triggered[()].connect(self.mapper.map) 

này là cần thiết cho các phiên bản sau của Python:

Python 2.6.6 (r266:84292, Feb 21 2013, 19:26:11) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2 
9

Bạn có thể sử dụng hàm lambda được kết hợp với khe điều khiển GUI để chuyển các đối số thừa vào phương thức bạn muốn thực thi.

# Create the build button with its caption 
self.build_button = QPushButton('&Build Greeting', self) 
# Connect the button's clicked signal to AddControl 
self.build_button.clicked.connect(lambda: self.AddControl('fooData')) 
def AddControl(self, name): 
    print name 

Nguồn: snip2code - Using Lambda Function To Pass Extra Argument in PyQt4

+1

Cảm ơn! Chính xác những gì tôi đang tìm kiếm! –

3

Cách tốt nhất để vượt qua các đối số là không vượt qua họ ở tất cả. Bạn có thể sử dụng tính năng động của python và đặt bất kỳ dữ liệu nào bạn cần làm thuộc tính của riêng bạn trên tiện ích con, nhận tiện ích mục tiêu trong trình xử lý bằng cách sử dụng self.sender() và sau đó nhận bất kỳ thuộc tính nào bạn cần trực tiếp từ tiện ích.

Trong ví dụ này năm nút được tạo ra và nhà nước cần được thiết lập trên widget nút như my_own_data tài sản:

import sys 
from PyQt4 import QtGui, QtCore 

class Main(QtGui.QMainWindow): 

    def __init__(self): 
     QtGui.QMainWindow.__init__(self) 
     self.centralwidget = QtGui.QWidget() 
     self.vboxlayout = QtGui.QVBoxLayout() 

     for idx in range(5): 
      button = QtGui.QPushButton('button ' + str(idx), None) 
      button.my_own_data = str(idx) # <<< set your own property 
      button.clicked.connect(self.click_handler) # <<< no args needed 
      self.vboxlayout.addWidget(button) 

     self.centralwidget.setLayout(self.vboxlayout) 
     self.setCentralWidget(self.centralwidget) 
     self.show() 

    def click_handler(self, data=None): 
     if not data: 
      target = self.sender() # <<< get the event target, i.e. the button widget 
      data = target.my_own_data # <<< get your own property 
     QtGui.QMessageBox.information(self, 
             "you clicked me!", 
             "my index is {0}".format(data)) 

app = QtGui.QApplication(sys.argv) 
main = Main() 
main.show() 

sys.exit(app.exec_()) 
+0

Có một số nhược điểm của phương pháp này, được đặt ra trong [tài liệu dành cho người gửi] (http://doc.qt.io/qt-4.8/qobject.html#sender). Như bạn đã viết nó, 'click_handler' không thể được gọi một cách an toàn bởi bất cứ điều gì khác hơn là các nút cụ thể được kết nối với nó. – ekhumoro

+0

@ekhumoro: Đồng ý; thêm tham số dữ liệu tùy chọn, để có thể sử dụng 'click_handler()' theo cách tổng quát hơn. – ccpizza

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