PySide: Asynchronously Text Filtering
QThreadを使った非同期テキストフィルタ
ほしいもの
- 別スレッドで実行されるテキストフィルタ
- 一定時間ごとにフィルタリング済みのデータを返す
- 途中でキャンセルもしたい
コード
from PySide import QtCore, QtGui import datetime import time class FilteringThread(QtCore.QThread): completed = QtCore.Signal() def __init__(self): super().__init__() self._data = [] # フィルタリングする前のデータリスト self._filter = '' # 部分一致キーワード self._queue = [] # フィルタ済みのデータ self._lastFlushed = datetime.datetime.now() # 前回データを流した時間 self._callback = None # フィルタ済みデータを受け取る関数 self._complete = True # 全体の処理が完了したか判定する self._canceled = False # 途中キャンセルがリクエストされたか def setFilter(self, text): # フィルタの設定 self._filter = text def setData(self, data): # データの設定 self._data = data def setCallback(self, func): # データ受取関数の設定 self._callback = func def cancel(self): # キャンセルのリクエスト self._canceled = True def isCompleted(self): # 終了判定 # isFinishedがいい感じで動かない return self._complete def run(self): # スレッドから実行される関数 self._complete = False self._canceled = False if self._filter: for d in self._data: if self._canceled: break time.sleep(0.001) # フィルタリングに時間がかかってるふり if self._filter in d: # フィルタを追加したらqueueにいれる self._queue.append(d) # 一定時間経過してたら流す now = datetime.datetime.now() if (now - self._lastFlushed).total_seconds() >= 1.0: self._callback(self._queue) self._queue.clear() self._lastFlushed = now # 最後に残ったデータを流して終了 self._callback(self._queue) self._queue.clear() self.completed.emit() self._complete = True self.exit() # 実験用データ DATA = [str(i) for i in range(0, 1000)] # 必要なインスタンスを作る app = QtGui.QApplication([]) listview = QtGui.QListWidget() filtertext = QtGui.QLineEdit() thread = FilteringThread() message = QtGui.QLabel('') # フィルタ変更時の処理 def textChanged(text): while not thread.isCompleted(): thread.cancel() time.sleep(0.1) listview.clear() if text == '': return thread.setFilter(text) thread.start() message.setText('') # フィルタ項目取得時 def flushed(queue): for i in queue: listview.addItem(QtGui.QListWidgetItem(i)) # 開始時 def started(): message.setText('filteringなう...') # 終了時 def completed(): message.setText('{0} matching records'.format(listview.count())) # 設定 listview.setUniformItemSizes(True) # ちょっぴり速くなるよ listview.setSortingEnabled(False) # もしソートするならflushedで一回だけやる filtertext.textChanged.connect(textChanged) thread.setData(DATA) thread.setCallback(flushed) thread.started.connect(started) thread.completed.connect(completed) # 実行 widget = QtGui.QWidget() widget.setLayout(QtGui.QVBoxLayout()) layout = widget.layout() layout.addWidget(filtertext) layout.addWidget(listview) layout.addWidget(message) widget.show() app.exec_()
表示するデータがあまりに多くなると今度はListWidgetのclearやらpaintやらで時間がかかってしまうようになるっぽい。これはどうしたものか…