スポンサーサイト

    上記の広告は1ヶ月以上更新のないブログに表示されています。
    新しい記事を書く事で広告が消せます。

    QThreadでストレスフルな生活を (並行処理って素敵だよネ)

     ファイルの一覧をGUIに表示させる場合など、階層が深いと非常に処理が遅くなる場合があります。
     特に内部で複雑なディレクトリの判定処理などを何重にも行なっていると表示までに時間がかかります。

     そこで、その裏側の処理を別スレッドにさせて、処理が終わった順に表示させて行く事により、ユーザーの感じる体感待ち時間を軽減させる処理を入れます。
     最近のアプリでは結構こういった裏処理をやってるものが多いですよね~。面倒ですが、やってるのかやってないかでは体感待ち時間が全然違うので是非頑張って入れてみたいところ!

     と言う事で今回は、QtのQThreadを使用して並行処理をしてみたいと思います。

    んじゃ、さっそくサンプルコードをば。
    # -*- coding:utf-8 -*-
    
    import sys
    import os
    import time
    from PyQt4 import QtGui, QtCore
    
    # Threading処理をするクラス。==================================================
    class FileLister( QtCore.QThread ):
        addObject = QtCore.pyqtSignal( str )
    
        def __init__( self, parent=None ):
            super( FileLister, self ).__init__( parent )
            self.startdir   = None
            self.stopped    = False
            self.mutex      = QtCore.QMutex()
    
        def setup( self, startdir ):
            self.startdir = startdir
            self.stoppped = False
    
        def stop( self ):
            with QtCore.QMutexLocker( self.mutex ):
                self.stopped = True
    
        def run( self ):
            cnt = 0
            for root, dirs, files in os.walk( self.startdir ):
                if self.stopped:
                    return
    
                for file in files:
                    filepath = os.path.join( root, file )
                    self.addObject.emit( filepath )
                    time.sleep( 0.1 )
                    cnt += 1
    
            print( 'Result : %s of files are detected.' % cnt )
            self.stop()
            self.finished.emit()
    # =============================================================================
    
    
    
    # インターフェースを構成するクラス。===========================================
    class UrlModel( QtGui.QStandardItemModel ):
        # パスリストのMimeTypeデータを受け取るためのItem Model。
        def mimeTypes( self ):
            return ['text/uri-list']
    
    class DroppableTreeView( QtGui.QTreeView ):
        # D&D可能なTreeViewクラス。
        def __init__( self, parent=None ):
            super( DroppableTreeView, self ).__init__( parent )
            self.setDragEnabled( True )
            self.setAcceptDrops( True )
            
            # Thread処理用インスタンス。-------------------------------------------
            self.fileLister = FileLister()
            self.fileLister.addObject.connect( self.addItem )
            # ---------------------------------------------------------------------
    
        def dragEnterEvent( self, event ):
            mimedata = event.mimeData()
            if mimedata.hasUrls():
                urilist = mimedata.urls()
                path    = str( urilist[0].path().toLocal8Bit() )[1:]
                if os.path.isdir( path ):
                    event.accept()
                else:
                    event.ignore()
            else:
                event.ignore()
        def dropEvent( self, event ):
            mimedata = event.mimeData()
            if not mimedata.hasUrls():
                event.ignore()
                return
    
            urilist = mimedata.urls()
            path    = str( urilist[0].path().toLocal8Bit() )[1:]
            
            # ドロップされたパスを制御処理スレッドに渡し、処理を実行。
            self.fileLister.setup( path )
            self.fileLister.start()
    
        def addItem( self, filepath ):
            item  = QtGui.QStandardItem( filepath )
            model = self.model()
            model.setItem( model.rowCount(), 0, item )
    # =============================================================================     
            
    
    
    # メインウィンドウクラス。=====================================================
    class MainWidget( QtGui.QWidget ):
        def __init__( self, parent=None ):
            super( MainWidget, self ).__init__( parent )
            self.resize( 450, 600 )
            self.move( 800, 400 )
            
            layout = QtGui.QVBoxLayout( self )
    
            # 注釈用ラベル。
            label  = QtGui.QLabel( 'Drop folder into this window.' )
    
            # D&D可能TreeViewの作成。----------------------------------------------
            self.treeview = DroppableTreeView()
    
            # Item Modelの作成。
            model = UrlModel( 0, 1 )
            model.setHeaderData( 0, QtCore.Qt.Horizontal, 'File Name' )
            self.treeview.setModel( model )
            
            layout.addWidget( label )
            layout.addWidget( self.treeview )
            # ---------------------------------------------------------------------
    # =============================================================================
            
    
    
            
    if __name__ == '__main__':
        app = QtGui.QApplication( sys.argv )
        win = MainWidget()
        win.show()
        
        sys.exit( app.exec_() )
    

    qtThread001.jpg
    完成~。フォルダをこのウィンドウにドロップすると、そのフォルダの階層下にあるファイルをすべて一覧としてリストします。
     当然数が多いと表示までに非常に時間がかかりますが、見つけた順にどんどん追加されてる最中にもリストをスクロールできたりするのがわかると思います。ストレス軽減(*^_^*)


     前回やったユーザー定義のシグナルが出てきます。
     QThreadを継承したクラスを作成し、そこにシグナルを定義します。

     やりたいことをrunメソッドの中に記述し、それによって生成されたオブジェクトを先ほど定義したシグナルからエミットしてやります。
     このシグナルにはあらかじめGUI本体の描画更新メソッド(今回の場合は87行目、DroppableTreeViewのaddItemメソッド)に接続しておき、処理終了後にこちらのメソッドに生成結果を渡します。


     QThreadにはstartメソッドがあるのですが、このstartメソッドの中でrunメソッドが実行されています。
     なので記述する必要があるのは基本的にrunメソッドだけですね。
     それと途中終了処理用にメソッドを用意しておきますが、この辺は基本的にテンプレですね。(面倒だから書かないのではないぞ! 本当だゾ!)


     今回のサンプルではファイルが増えていく様子が分かりやすいように、ファイルを検知する度に0.1秒ほどスリープをかけてます。
     うぉぉぉぉぉ、これからはThreadの時代だぁ~!!!w
    スポンサーサイト

    コメントの投稿

    非公開コメント

    プロフィール

    Eske

    Author:Eske
    萌えイラストレーターを目指す3DCGイラストレーター。
    現在ポケモンカードゲーム、ガンダムトライエイジ、ガンダムコンクエスト、妖怪ウォッチとりつきカードゲームなどで3DCGを使用したイラストレーターとして参加中。

    主にここでは日々気づいたメモなんかを残してます。
    イラストのお仕事も受け付けております。ココからアクセスできますので、お気軽にご相談下さい。

    最新記事
    最新コメント
    カテゴリ
    最新トラックバック
    月別アーカイブ
    検索フォーム
    リンク
    QRコード
    QR
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。