2016-07-15 5 views
1

QtDesigner(SourceForm、DestinationForm)に2つのウィンドウを作成し、それらの.uiページを変換するためにpyuic5を使用しました。私はスタックウィジェットを使用して2つのウィンドウ間を移動する方法として、第3のクラスWControllerを使用しています。 SourceFormには、treeWidgetに何らかのデータが入力され、handle_treewidget_itemchangeのメソッドは、self.treeWidget.itemChanged.connect(self.handle_treewidget_itemchange)を使用してtreeWidgetの特定の項目をチェックするかチェックしないかを指定します。 itemChanged.connectが自動的にスロットに変更されたかの行と列を送信することを私の理解だったが、handle_treewidget_itemchange(self,row,col)が最初に呼び出されたときに、私のスクリプトは、TypeError例外でクラッシュ:私が取る場合PyQt5初心者 - シグナルと欠落した位置引数

TypeError: handle_treewidget_itemchange() missing 2 required positional arguments: 'row' and 'col'

rowcol argsの場合、スクリプトはうまく動作します。もともと私はSourceForm .pyファイル自体にメソッドと呼び出しの両方を持っていましたが、私のコードは意図したとおりに動作しました...これは単なるスコープの問題ですか?

from PyQt5 import QtCore, QtWidgets 
from PyQt5.QtCore import pyqtSlot 
from imp_sourceform import Ui_SourceForm 
from imp_destform import Ui_DestinationForm 


class WController(QtWidgets.QMainWindow): 
    def __init__(self, parent=None): 
     super(WController, self).__init__(parent) 
     self.central_widget = QtWidgets.QStackedWidget() 
     self.setCentralWidget(self.central_widget) 

     self.sourcewindow = SourceForm() 
     self.destinationwindow = DestinationForm() 

     self.central_widget.addWidget(self.sourcewindow) 
     self.central_widget.addWidget(self.destinationwindow) 

     self.central_widget.setCurrentWidget(self.sourcewindow) 

     self.sourcewindow.selectdestinationsbutton.clicked.connect(lambda: self.navigation_control(1)) 
     self.destinationwindow.backbutton.clicked.connect(lambda: self.navigation_control(0)) 

    def navigation_control(self, topage): 
     if topage == 1: 
      self.central_widget.setCurrentWidget(self.destinationwindow) 
     elif topage == 0: 
      self.central_widget.setCurrentWidget(self.sourcewindow) 

class SourceForm(QtWidgets.QWidget, Ui_SourceForm): 
    def __init__(self): 
     super(SourceForm, self).__init__() 
     self.setupUi(self) 

     self.treeWidget.itemChanged.connect(self.handle_treewidget_itemchange) 

    @pyqtSlot() 
    def handle_treewidget_itemchange(self,row,col): 
     if row.parent() is None and row.checkState(col) == QtCore.Qt.Unchecked: 
      for x in range(0,row.childCount()): 
       row.child(x).setCheckState(0, QtCore.Qt.Unchecked) 
     elif row.parent() is None and row.checkState(col) == QtCore.Qt.Checked: 
      for x in range(0,row.childCount()): 
       row.child(x).setCheckState(0, QtCore.Qt.Checked) 
     else: 
      pass 

class DestinationForm(QtWidgets.QWidget, Ui_DestinationForm): 
    def __init__(self): 
     super(DestinationForm, self).__init__() 
     self.setupUi(self) 

if __name__ == '__main__': 
    import sys 
    app = QtWidgets.QApplication(sys.argv) 
    window = WController() 
    window.show() 
    sys.exit(app.exec_()) 

答えて

-1

使用するときは注意する必要があります:私はダウン必需品にコードを除去しようとしたのPythonと、まだ経験の浅い悪いアイデア:(

ながら、私はPyQtはを使用しようと考え始めていますpyqtSlotあなたのケースでは、スロットが引数を持たないものとして再定義されているため、そのエラーメッセージが表示される理由を説明しています。あなたの例がそれなしで完全にうまくいくので、単にそれを削除してください。

の主な目的は、スロットのいくつかの異なる過負荷を定義し、それぞれ異なる署名で行うことができます。また、クロススレッド接続を行うときに必要になることもあります。しかし、これらのユースケースは比較的まれであり、ほとんどのPyQt/PySideアプリケーションでは、pyqtSlotをまったく使用する必要はありません。信号は、 python呼び出し可能オブジェクトに、スロットとして装飾されているかどうかにかかわらず接続できます。

+0

pyqtSlotが本当のQtのスロットを作成します。

を使用すると、1つの接続または他を有効にすることができ、このコードを参照してください、そして窓がガベージコレクトできるか否かを示す動作の違いを観察しますスロットを持つQObjectが破壊/ガベージコレクトされると(Qtは削除されたQObjectから何かを切断する)、接続は切断されます。 '@ pyqtSlot'を使わない場合、接続はQObjectを追跡することができないので、呼び出し可能な参照を保持してQObjectへの参照を保持し、破壊。 IMHO可能であれば '@ pyqtSlot'を使うのが好きです。 –

+0

ああ、恥ずかしいです、それは今働いています!どうもありがとうございます! '@pyqtSlot()'デコレータを使用していたので、簡単にスロットを見つけることができました。私(危険な)はそれがその形で良性であると仮定した。 私はもともと 'handle_treewidget_itemchange'メソッドとその接続ステートメントを' SourceForm'に入れました(GUIを微調整したいと思ってすぐに学んだのは恐ろしいアイデアでした)。そして、スロットは行と列の引数を 'pyqtslot 'デコレータはそのままです...だから私はそれを潜在的な修正として削除することは考えていませんでした。 –

+0

私はちょうど私が言ったことをテストしました:実際のスロットに接続するhttp://pastebin.com/W5tFkWGLは、 "任意の呼び出し可能"をスロットとして使用するのとは異なり、参照カウントを妨げません。ですから、私は本当に "呼び出し可能"を 'connect'のターゲットとして使うのは悪い考えです。可能ならば避け、' pyqtSlot'で置き換えるのが良いと思います。 –

0

この質問に対する回答はすでに受け入れられていますが、とにかく私は私に与えます。

問題のあるスロットはitemChanged(QTreeWidgetItem *item, int column)信号に接続されているため、pyqtSlot@pyqtSlot(QTreeWidgetItem, int)のようになります。

今や、ekhumoroが指摘されているように、PyQtはメソッド、ラムダ、または__call__メソッドを持つファンクタである任意のPython呼び出し可能関数にシグナルを接続することができます。しかし、@pyqtSlotを使用するのではなく、それほど安全ではありません。

たとえば、発信元QObject(発信者)が破棄されるか、(Qtスロットを持つ)対象QObjectが破棄されると、Qtは自動的に切断します。たとえば、ウィジェットを削除した場合、別の場所で何か起こったことを伝える必要はありません。 @pyqtSlotを使用すると、実際のQtスロットがクラスに作成されるため、この切断メカニズムが適用されます。また、QtはターゲットQObjectへの強い参照を保持しないので、削除することができます。

呼び出し可能な、例えば装飾されていないバインドされたメソッドを使用する場合、Qtは接続のタ​​ーゲットQObjectを識別する方法を持ちません。さらに悪いことに、Python呼び出し可能コードを渡すので、呼び出し元(バインドされたメソッド)は最終的なQObjectへの参照を保持します。したがって、手動で手続きするまで、ターゲットQObjectはガベージコレクションされません切断するか、ソースQObjectを削除してください。、

from PyQt5.QtWidgets import QApplication, QMainWindow 

app = QApplication([]) 

def create_win(): 
    win = QMainWindow() 
    win.show() 

    # case 1 
    # app.aboutToQuit.connect(win.repaint) # this is a qt slot, so win can be deleted 

    # case 2 
    # app.aboutToQuit.connect(win.size) # this is not a qt slot, so win can't be deleted 

    # win should get garbage-collected here 

create_win() 

app.exec_() 
+0

スロットとして装飾されていない呼び出し可能コードに接続*決して*するべきではないことを真剣に提案していますか?それは任意かつ完全に不要な制限のようです。独自の例で 'win.size'のような、私たちが制御できない呼び出し可能ファイルはどうでしょうか? pyqtプログラムでガベージコレクションを心配する必要があるのは、実際に測定可能な問題が発生し始めたときだけです。あなたの例はそれを示していないので、あなたがここで何をしようとしているのか分かりません。とにかくアプリケーションの存続期間中はメインウィンドウが生きているはずですので、それを明示的に削除するのはほとんど意味がありません。 – ekhumoro

+0

@ekhumoro私は「決して」と言っているのではなく、「可能な限り避けてください」と言っています。そして、OPの場合、それは簡単です。私の例は遠いものです、はい、私が言ったことを実証する最小限のテストケースです。いいえ、より複雑なシナリオで微妙に発生するため、実際の問題のインスタンスではありません。ここでは、私の例を通常の永続的なQMainWindowで転置し、障害のあるオブジェクトは環境設定のQDialogです。環境設定ダイアログが閉じられると、リファレンスはそのメソッドの1つに保持されるため、削除されません。そして、QDialogは信号の受信を続けます... –

+0

@ekhumoro私はより現実的な例を作りようとしましたが、私が部分的に間違っていることを発見しました。オブジェクトへの参照は保持しませんが、C++メソッドは次のようにします:http://pastebin.com/XS4q2aHu 'size'のようなC++メソッドが使われているが、Pythonのメソッド(' faultySlot ')。 –