PyGTKのプログレスバーとスレッド

最近、PyGTKについて知りたいという要望がありました。 この問題に関するロシア語の文献は事実上存在せず、Googleがさまざまなブログで見つけたものは少し時代遅れです。 また、PyGTKテーマがハブでもあまり人気がないことを知って驚きました。



そのため、インターフェイス要素のレイアウトについては説明しません。そのような記事は既に存在しているからです。 次のステップについて説明します。進行状況を表示するプロセスで、何らかの作業を行うアプリケーションを作成します。



たとえば、変換ユーティリティ(ImageMagickパッケージ)の2つの機能のプリミティブGUIを作成します。 私たちのプログラムは4つの価値を取ります:



インターフェイス自体は、空き地で作成されます。 重要なのは、プロジェクトがGtkBuilder形式である必要があることです。



Glade GUI








以下に2つのコード例を示します。1つ目は普通のもので、2つ目は画像処理用の別のスレッドを作成します。 2つの例があります-スレッドをいじることが理にかなっているかどうかを明確に確認するためです。



スケルトンプログラム:



#!/usr/bin/python

# coding: utf-8



try :

import sys , pygtk

pygtk . require ( '2.0' )

except :

print ' PyGTK'

sys . exit ( 1 )



import gtk , os , time



class GUI ( object ) :

def __init__ ( self ) :

self . wTree = gtk . Builder ( )

#

self . wTree . add_from_file ( "convert.glade" )

#

self . wTree . connect_signals ( self )

self . window1 = self . wTree . get_object ( "window1" )

self . progressdialog = self . wTree . get_object ( "progressdialog" )

self . progressbar_label = self . wTree . get_object ( "progressbar_label" )

self . window1 . show ( )

#

self . wTree . get_object ( "size" ) . set_value ( 100 )

self . wTree . get_object ( "quality" ) . set_value ( 95 )



self . progressbar = self . wTree . get_object ( "progressbar" )



def on_cancel ( self , widget ) :

gtk . main_quit ( )



def on_progressdialog_close ( self , * args ) :

self . stop = True

self . progressdialog . hide ( )

return True



if __name__ = = "__main__" :

app = GUI ( )

gtk . main ( )









メインのon_startメソッドを追加します。これは、プログレスバーダイアログを表示し、ユーザー指定の値を受け取り、ファイルのリストを生成し(ディレクトリを除く)、処理に直接関与します。



def on_start ( self , widget ) :

self . progressdialog . show ( )

self . stop = False

# GUI

self . size = int ( self . wTree . get_object ( "size" ) . get_value ( ) )

self . quality = int ( self . wTree . get_object ( "quality" ) . get_value ( ) )

self . from_dir = self . wTree . get_object ( "from_dir" ) . get_current_folder ( )

self . to_dir = self . wTree . get_object ( "to_dir" ) . get_current_folder ( )



files = [ ]

all_files = os . listdir ( self . from_dir )

for f in all_files :

fullname = os . path . join ( self . from_dir , f )

if os . path . isfile ( fullname ) :

files . append ( f )



count = len ( files )

i = 1.0

for file in files :

#

if self . stop :

break



self . progressbar_label . set_text ( file )

self . progressbar . set_fraction ( i / count )



os . popen ( 'convert -resize ' + str ( self . size ) + ' -quality ' + str ( self . quality ) + ' ' + os . path . join ( self . from_dir , file ) + ' ' + os . path . join ( self . to_dir , file ) )



#

while gtk . events_pending ( ) :

gtk . main_iteration ( )



time . sleep ( 5 )

i + = 1



self . progressdialog . hide ( )









ダイアログボックスを表示し、GUIから値を読み込み、ファイルのリストを生成します。 次に、ファイルリストを繰り返し処理するループを実行します。



まず、self.stopの処理の終了を示す停止フラグがあるかどうかを確認します(キャンセルボタンのon_progressdialog_closeメソッドで設定するか、ダイアログボックスを閉じます)。 さらに、プロセスでは、進行状況バーのテキストと処理の割合を変更し、必要なパラメーターを使用して変換ユーティリティ自体を実行します。



重要なコード

while gtk . events_pending ( ) :

gtk . main_iteration ( )









これがないと、ダイアログボックスが表示されず、処理が完了するまでインターフェイスがフリーズします。 これは、ループが完了するまでインターフェイスの再描画(メインループ)をブロックするためです。 上記のコードは、短時間メインループに制御を戻します。



また、具体的にtime.sleep(5)を追加しました。高速コンピューターを使用している場合、処理中(またはスリープ中)にインターフェイスがイベントに応答しないことに気付かない場合があります。



スレッド



PyGTKのスレッドでは、知らないうちにいじらなければなりませんでした。 まず、gtk.gdk.threads_init()を呼び出す必要があり、さらにgtkへのすべての呼び出しは、gtk.threads_enter()およびgtk.threads_leave()によってフレーム化される必要があります。



#!/usr/bin/python

# coding: utf-8



try :

import sys , pygtk

pygtk . require ( '2.0' )

except :

print ' PyGTK'

sys . exit ( 1 )



import threading , gtk , os , time



class GUI ( object ) :

def __init__ ( self ) :

self . wTree = gtk . Builder ( )

#

self . wTree . add_from_file ( "convert.glade" )

#

self . wTree . connect_signals ( self )

self . window1 = self . wTree . get_object ( "window1" )

self . dialog = {

"progressdialog" : self . wTree . get_object ( "progressdialog" ) ,

"progressbar_label" : self . wTree . get_object ( "progressbar_label" ) ,

"progressbar" : self . wTree . get_object ( "progressbar" )

}

self . window1 . show ( )

#

self . wTree . get_object ( "size" ) . set_value ( 100 )

self . wTree . get_object ( "quality" ) . set_value ( 95 )





def on_cancel ( self , widget ) :

gtk . main_quit ( )



def on_progressdialog_close ( self , * args ) :

self . work . stop ( )

self . dialog [ 'progressdialog' ] . hide ( )

return True



def on_start ( self , widget ) :

self . dialog [ 'progressdialog' ] . show ( )

#

self . data = {

'size' : int ( self . wTree . get_object ( "size" ) . get_value ( ) ) ,

'quality' : int ( self . wTree . get_object ( "quality" ) . get_value ( ) ) ,

'from_dir' : self . wTree . get_object ( "from_dir" ) . get_current_folder ( ) ,

'to_dir' : self . wTree . get_object ( "to_dir" ) . get_current_folder ( )

}



files = [ ]

all_files = os . listdir ( self . data [ 'from_dir' ] )

for f in all_files :

fullname = os . path . join ( self . data [ 'from_dir' ] , f )

if os . path . isfile ( fullname ) :

files . append ( f )



self . work = Worker ( self . dialog , self . data , files )

self . work . start ( )





if __name__ = = "__main__" :

gtk . gdk . threads_init ( )

app = GUI ( )

gtk . gdk . threads_enter ( )

gtk . main ( )

gtk . gdk . threads_leave ( )









コードが少し変更されました。 self.dialogディクショナリを作成しました。これは以下で役立ちます。on_startメソッドは、データを準備して新しいスレッドを起動するだけで、直接処理はプログラムのインターフェイスをブロックしません。



Workerクラスを作成します。

class Worker ( threading . Thread ) :

#

stopthread = threading . Event ( )



def __init__ ( self , dialog , data , files ) :

threading . Thread . __init__ ( self )

self . dialog = dialog

self . data = data

self . files = files



def run ( self ) :

count = len ( self . files )

i = 1.0

for file in self . files :

#

if ( self . stopthread . isSet ( ) ) :

self . stopthread . clear ( )

break



self . dialog [ 'progressbar' ] . set_fraction ( i / count )

self . dialog [ 'progressbar_label' ] . set_text ( file )



os . popen ( 'convert -resize ' + str ( self . data [ 'size' ] ) + ' -quality ' + str ( self . data [ 'quality' ] ) + ' ' + os . path . join ( self . data [ 'from_dir' ] , file ) + ' ' + os . path . join ( self . data [ 'to_dir' ] , file ) )



time . sleep ( 2 )

i + = 1

#

self . stopthread . clear ( )

#

self . dialog [ 'progressdialog' ] . hide ( )



def stop ( self ) :

self . stopthread . set ( )









パラメーターを受け入れるように、コンストラクター(__init__)を再定義する必要がありました。 これらは、ダイアログ辞書(私が言った、便利になるだろう)、データ(サイズ、品質、2つのディレクトリ)、およびファイルのリストです。

runメソッドには、起動時に実行する必要があるもの、つまり 処理自体。



「顔の結果」と言うように、それだけです。



この例では、対話の完了率とその変化の計算は、作業スレッドで直接行われます。 これは不正な形式と見なされ、複数のスレッドが存在する場合は機能しません(より正確には、エラーが発生しても機能します)。



プログラムのわずかに拡張されたバージョンは次のようになります



シンプルな画像コンバーター








機能について読んで、 ここからダウンロードしてください



ソースコードでアーカイブします



UPD:

alex3dは、マニュアルに一致する場合、Worker.runで次の行を実行することを思い出しました

self.dialog['progressbar'].set_fraction(i/count)

self.dialog['progressbar_label'].set_text(file)






包む必要がある

gtk.threads_enter()/ gtk.threads_leave()。



All Articles