Python lambda式の落とし穴
Pythonのクロージャ内部で使われている変数は通常、実行時に値が評価されます。for文などと組み合わせてlambda式やローカル関数を使うときは、ちょっと気を付けないといけません。
例えば、0~4まで出力する関数を5つ用意したいとき
# 関数作る functions = [] for i in range(0, 5): functions.append(lambda: print(i)) # 関数呼び出す for func in functions: func() # この時点では i は 4 ですよ?
このように書いてしまうと、下記のような結果が出力されてしまいます。
4 4 4 4 4
こいつを回避するためには、デフォルト引数を使ってちょいと工夫してやる必要があります。
# 関数作る functions = [] for i in range(0, 5): functions.append(lambda x=i: print(x)) # デフォルト引数は関数定義時に評価される # 関数呼び出す for func in functions: func()
これで意図した結果が得られます。
0 1 2 3 4
functools.partialを使う方法もあるようですね。
QDockWidgetに追加したWidgetが表示されないとき
setWidget()を使え
QListWidget コンテキストメニュー
# conding: utf-8 import sys from PySide.QtCore import * from PySide.QtGui import * # 右クリックでカレントの行を削除 def customMenu(widget, pos): if len(widget.selectedItems()) == 0: return action = QAction('Remove', widget) action.triggered.connect(lambda : widget.takeItem(widget.currentRow())) menu = QMenu() menu.addAction(action) menu.exec_(widget.mapToGlobal(pos)) # entry point if __name__ == '__main__': myapp = QApplication(sys.argv) widget = QListWidget() widget.setContextMenuPolicy(Qt.CustomContextMenu) # Policyも設定しないといけない widget.customContextMenuRequested.connect(lambda pos : customMenu(widget, pos)) widget.addItem('item1') widget.addItem('item2') widget.addItem('item3') widget.show() sys.exit(myapp.exec_())
QScrollAreaの罠にハマる
いつものようにLayoutにButtonを追加するが
一定サイズ以下に縮まないしスクロールバーも出ない
# conding: utf-8 import sys from PySide.QtCore import * from PySide.QtGui import * # entry point if __name__ == '__main__': myapp = QApplication(sys.argv) widget = QScrollArea() widget.setWidgetResizable(True) # いつもの layout = QVBoxLayout(widget) widget.setLayout(layout) layout.addWidget(QPushButton('OK!')) layout.addWidget(QPushButton('OK!')) layout.addWidget(QPushButton('OK!')) layout.addWidget(QPushButton('OK!')) layout.addWidget(QPushButton('OK!')) layout.addWidget(QPushButton('OK!')) layout.addWidget(QPushButton('OK!')) layout.addWidget(QPushButton('OK!')) widget.show() sys.exit(myapp.exec_())
Widgetを一つはさむと何故か想定どおりに動く
# 内側にWidgetを1つ作ってあげる
inner = QWidget()
layout = QVBoxLayout(inner)
inner.setLayout(layout)
widget.setWidget(inner)
QListWidget 文字列フィルタリングしてみる
文字列でフィルタリングできるリストビューがあれば
人生の複雑な状況も整理できるかもしれない
# conding: utf-8 import sys from PySide.QtCore import * from PySide.QtGui import * class MyListWidget(QWidget): def __init__(self, data, parent=None): super().__init__(parent) layout = QVBoxLayout() self.__filter = QLineEdit() self.__lsview = QListWidget() self.setLayout(layout) layout.addWidget(self.__filter) layout.addWidget(self.__lsview) self.__filter.textChanged.connect(self.__refresh) self.__data = data self.__refresh() def __refresh(self): self.__lsview.clear() # 文字列でフィルタリング s = self.__filter.text() for d in self.__data: if len(s) > 0 and s not in d: continue self.__lsview.addItem(QListWidgetItem(d)) # entry point if __name__ == '__main__': myapp = QApplication(sys.argv) widget = QWidget() layout = QHBoxLayout(widget) # ややこしいデータを挿入する mylist = MyListWidget(( '俺がお前でお前が俺で嫁は関係ない', '俺は俺でお前はお前で嫁は嫁だからあんまり関係ない', 'お前のものは俺のもので俺のものは嫁のもの', '俺もお前でお前も俺で嫁は関係ない', '俺もお前もあいつもこいつも嫁も関係ない', '俺とお前が嫁で嫁は実は婿だった訳だ', 'お前の嫁が俺の嫁なわけがない', 'えっ?俺がお前の嫁なの?')) widget.setLayout(layout) layout.addWidget(mylist) widget.show() sys.exit(myapp.exec_())
駄目でした
QComboboxにはOrderedDictが良さげ
Comboboxのインデックスと処理が1:1だと便利
dictは追加した要素を不定の順序でイテレートするので
挿入順を保存したい場合は OrderedDict を使う
# conding: utf-8 import sys from PySide.QtCore import * from PySide.QtGui import * import collections # entry point if __name__ == '__main__': myapp = QApplication(sys.argv) widget = QWidget() layout = QHBoxLayout(widget) lhs = QSpinBox() ope = QComboBox() rhs = QSpinBox() btn = QPushButton('Calc') ans = QLabel('???') widget.setLayout(layout) layout.addWidget(lhs) layout.addWidget(ope) layout.addWidget(rhs) layout.addWidget(btn) layout.addWidget(ans) # 辞書に追加した順序が保持される operators = collections.OrderedDict(( ('Add', lambda x, y : x + y), ('Sub', lambda x, y : x - y), ('Mul', lambda x, y : x * y), ('Div', lambda x, y : x / y), )) # コンボボックスに追加 ope.addItems(list(operators.keys())) # 計算ボタンがおされたときの処理 def calc(): x = operators[ ope.currentText() ](lhs.value(), rhs.value()) ans.setText(str(x)) # ボタン押下イベントに接続 btn.clicked.connect(calc) widget.show() sys.exit(myapp.exec_())
Signalの使いかたをよく忘れるのでメモ
# conding: utf-8 import sys from PySide.QtCore import * from PySide.QtGui import * # ほげ~ class Hoge(QLabel): # 文字列を引数に渡すシグナルを定義 textChanged = Signal(str) # 初期化 def __init__(self, parent=None): super().__init__(parent) # setTextが呼ばれたらラベルを書き換えて一秒後にシグナルを発信 def setText(self, text): super().setText(text) QTimer.singleShot(1000, lambda : self.textChanged.emit(text)) # entry point if __name__ == '__main__': myapp = QApplication(sys.argv) widget = QWidget() layout = QVBoxLayout(widget) lineed = QLineEdit() label0 = Hoge() label1 = Hoge() label2 = Hoge() label3 = Hoge() label4 = Hoge() widget.setLayout(layout) layout.addWidget(lineed) layout.addWidget(label0) layout.addWidget(label1) layout.addWidget(label2) layout.addWidget(label3) layout.addWidget(label4) lineed.textChanged.connect(lambda s : label0.setText(s)) label0.textChanged.connect(lambda s : label1.setText(s)) label1.textChanged.connect(lambda s : label2.setText(s)) label2.textChanged.connect(lambda s : label3.setText(s)) label3.textChanged.connect(lambda s : label4.setText(s)) widget.show() sys.exit(myapp.exec_())