
今回はKivyというPythonのGUIライブラリを使って、画像をスライドショー表示するピクチャービューワーを作成してみました。前回、icrawlerを使って画像を自動的にダウンロードするプログラムを作りましたが、それと組み合わせることで、好みの画像をスライドショーできるようになります。
プログラムを実行した様子はこちらです。
準備
今回のプログラムでは当然ながらkivyモジュールを使います。以下のコマンドでインストールしてください。
|
1 |
pip install kivy |
またメッセージボックスの表示にpyautoguiモジュールを使っているので、こちらもインストールしてください。
|
1 |
pip install pyautogui |
プログラムの内容
ソースコードはこちら。
main.py
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
#-*- coding: utf-8 -*- from kivy.config import Config Config.set('graphics', 'width', '1024') Config.set('graphics', 'height', '768') from kivy.app import App from kivy.clock import Clock from kivy.uix.widget import Widget from kivy.properties import StringProperty from kivy.core.text import LabelBase, DEFAULT_FONT from kivy.resources import resource_add_path import pyautogui as pg import os import re from glob import glob from random import randint #日本語が使用できるように日本語フォントを指定する resource_add_path('C:\Windows\Fonts') LabelBase.register(DEFAULT_FONT, 'meiryo.ttc') resource_add_path('./images') class ImageViewer(Widget): b_slideshow = False # True:スライドショー、False:画像固定 event = None source = StringProperty("./default.jpg") def __init__(self, **kwargs): super(ImageViewer, self).__init__(**kwargs) self.dir = r"./images/" self.filenames = [] for filename in glob(self.dir + '*.*'): f = os.path.basename(filename) if re.search(r'jpg|jpeg|png', f): self.filenames.append(filename) if len(self.filenames) == 0: pg.alert(text='画像ファイルが存在しません', button='OK') exit() self.source= self.filenames[0] self.current_pict = 0 self.ViewModeToggle() def ViewModeToggle(self): if self.b_slideshow == False: self.b_slideshow = True self.event = Clock.schedule_interval(self.RandomPictureSchedule, 2.0) if self.ids.items(): self.ids["button1"].text = '画像を固定する' else: self.b_slideshow = False self.event.cancel() if self.ids.items(): self.ids["button1"].text = 'スライドショー' def RandomPictureSchedule(self, dt): self.RandomPicture() def RandomPicture(self): n = len(self.filenames) if n != 1: while True: i = randint(0, n-1) if i != self.current_pict: # 違う画像になるまでループで回す self.current_pict = i break self.source = self.filenames[i] class MyKivyApp(App): def __init__(self, **kwargs): super(MyKivyApp, self).__init__(**kwargs) self.title = 'Photo Viewer' if __name__ == '__main__': MyKivyApp().run() |
mykivy.kv
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
ImageViewer: <ImageViewer>: canvas.before: Color: rgba: 0.7, 0.7, 0.7, 1 Rectangle: pos: self.pos size: self.size BoxLayout: orientation: 'vertical' size: root.size Image: source: root.source BoxLayout: size_hint_y: 0.15 padding: 20,30,30,20 Button: id: button1 text: "画像を固定する" font_size: 30 on_press: root.ViewModeToggle() Button: id: button2 text: "ランダム表示" font_size: 30 on_press: root.RandomPicture() |
ソースコードはこちらからダウンロードできます。
KivyではPythonプログラムは必ずmain.pyというファイル名でプログラムを作ります。また、Appクラスを継承したクラス(今回はMyKivyAppクラス)の「App」を除いた名前を小文字にしたもの(今回はmykivy)をファイル名にしたkvファイル(mykivy.kv)ファイルを作成します。こちらにGUIについての記述をします。
また今回のプログラムは、プログラム(main.pyおよびmykivy.kv)と同じディレクトリにある「images」フォルダ内にある画像をスライドショーで表示します。
さらに(必須ではありませんが)前回作った画像をダウンロードするプログラム(get_images_GUI.py)と組み合わせて使うことを想定しています。

先に示した映像では以下のように動作させています。
- 前回作成した画像をダウンロードするプログラム(get_images_GUI.py)を実行して、画像をダウンロードする。
- 今回のKivyプログラムを実行して、imagesフォルダにある画像を表示する。
このようにすれば、その日の気分で見たい画像をダウンロードして、スライドショーすることができます。
get_images_GUI.pyのソースコードはこちら。
プログラムの説明(main.py)
それでは簡単に内容を説明していきます。
main.pyではImageViewerという名前のWidgetクラスと、MyKivyAppというAppクラスを作成しています。Appクラスがプログラムの大枠(ウィンドウの表示など)で、その中での個々の動作(画像の表示やボタンに対する処理など)をWidgetクラスが担っているという感じでしょうか。
|
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from kivy.config import Config Config.set('graphics', 'width', '1024') Config.set('graphics', 'height', '768') from kivy.app import App from kivy.clock import Clock from kivy.uix.widget import Widget from kivy.properties import StringProperty from kivy.core.text import LabelBase, DEFAULT_FONT from kivy.resources import resource_add_path import pyautogui as pg import os import re from glob import glob from random import randint #日本語が使用できるように日本語フォントを指定する resource_add_path('C:\Windows\Fonts') LabelBase.register(DEFAULT_FONT, 'meiryo.ttc') resource_add_path('./images') |
まずアプリ起動時のウィンドウサイズを1024 x 768に指定しています(3,4行目)。
必要なモジュールをインポートしたあと、日本語が表示できるようにフォントを指定しています(19,20行目)。ここではメイリオ(meiryo.ttc)を使っています。
また画像があるフォルダのパスを指定しています(22行目)。このあと説明しますが、実際にはこのフォルダ名を含めてファイル名をリストにしていますので、ここでのパス指定は不要かもしれません。
|
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
class ImageViewer(Widget): b_slideshow = False # True:スライドショー、False:画像固定 event = None source = StringProperty("./default.jpg") def __init__(self, **kwargs): super(ImageViewer, self).__init__(**kwargs) self.dir = r"./images/" self.filenames = [] for filename in glob(self.dir + '*.*'): f = os.path.basename(filename) if re.search(r'jpg|jpeg|png', f): self.filenames.append(filename) if len(self.filenames) == 0: pg.alert(text='画像ファイルが存在しません', button='OK') exit() self.source= self.filenames[0] self.current_pict = 0 self.ViewModeToggle() def ViewModeToggle(self): if self.b_slideshow == False: self.b_slideshow = True self.event = Clock.schedule_interval(self.RandomPictureSchedule, 2.0) if self.ids.items(): self.ids["button1"].text = '画像を固定する' else: self.b_slideshow = False self.event.cancel() if self.ids.items(): self.ids["button1"].text = 'スライドショー' def RandomPictureSchedule(self, dt): self.RandomPicture() def RandomPicture(self): n = len(self.filenames) if n != 1: while True: i = randint(0, n-1) if i != self.current_pict: # 違う画像になるまでループで回す self.current_pict = i break self.source = self.filenames[i] |
ImageViewerクラスでは、コンストラクタ(__init__関数)で、「images」フォルダにある画像ファイル(拡張子がjpg、jpeg、pngのもの)をリスト変数 filenames に格納しています(34~39行目)。
もしも「images」フォルダに画像がない場合はメッセージを出してプログラムを終了させています(41~43行目)。画像があった場合は最初の画像ファイルを変数 source に指定しています(45行目)。変数 current_pict は現在表示されている画像ファイルのリスト内での番号です(46行目)。その後、ViewModeToggle 関数を呼び出しています(47行目)。
ViewModeToggle関数では、ブール変数 b_slideshow をチェックして、FalseであればTrueに変えてスライドショーを実行します。TrueであればFalseに変えて画像を固定します(スライドショーの中断)。またボタンの表記も変化させています。
画像のスライドショーはClockモジュールの schedule_interval 関数を使って、2秒ごとに RandomPictureSchedule 関数を呼び出すようにしています(53行目)。
RandomPictureSchedule 関数は RandomPicture 関数を呼び出しています。なぜ直接 RandomPicture 関数をClock.schedule_interval関数で指定しなかったのだろうと思われる人がいるかと思います。その理由は、Clock.schedule_interval関数で定期的な呼び出しを指定する関数は、前回呼び出し時間からの経過時間を表す引数 dt を取る必要があるのですが、RandomPicture 関数はGUIでボタンが押されたときにも呼び出しに使っていて、引数を(self以外に)もたないからです。そのため引数 dt を持つ RandomPictureSchedule 関数を間に挟んでいます(もっとスマートな書き方があれば教えてください)。
RandomPicture 関数では、「images」フォルダに画像が複数枚ある場合は、現在表示されている画像とは違う画像をランダムに選んで、画像ファイルを示す変数source を更新しています(67~73行目)。
|
76 77 78 79 |
class MyKivyApp(App): def __init__(self, **kwargs): super(MyKivyApp, self).__init__(**kwargs) self.title = 'Photo Viewer' |
MyKivyAppクラスはAppクラスを継承して、コンストラクタ(__init__関数)を呼び出しているだけです。変数 titleにプログラムのメインウィンドウに表示されるアプリ名を指定しています。
プログラムの説明(mykivy.kv)
つぎにkvファイルについて説明していきます。
|
3 4 5 6 7 8 9 10 |
<ImageViewer>: canvas.before: Color: rgba: 0.7, 0.7, 0.7, 1 Rectangle: pos: self.pos size: self.size |
まず、不透明な灰色(RGBA = 0.7, 0.7, 0.7, 1.0)で、ウィンドウの位置にウィンドウと同じサイズの矩形(Rectangle)を描画しています。これでウィンドウの背景色が灰色になります。
|
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
BoxLayout: orientation: 'vertical' size: root.size Image: source: root.source BoxLayout: size_hint_y: 0.15 padding: 20,30,30,20 Button: id: button1 text: "画像を固定する" font_size: 30 on_press: root.ViewModeToggle() Button: id: button2 text: "ランダム表示" font_size: 30 on_press: root.RandomPicture() |
次にBoxLayoutを使って、いくつかのWidget(Imageウィジェットおよび2つのButtonウィジェット)を追加しています。イメージで説明すると以下のような配置になります。縦方向(vertical)のBoxLayoutの上の段にImageウィジェット、下の段に横方向(horizontal)のBoxLayoutが配置されています。下の段のBoxLayoutの中に2つのButtonウィジェットが横方向に配置されています(BoxLayoutのデフォルト方向がhorizontalなので、2つ目のBoxLayoutではorientationは省略してあります)。また size_hint_y によって縦方向の比率を指定しています。

また、下の段のBoxLayoutはpaddingによって下図のように間が空いています(単位はピクセルです)。左右で幅がズレているのは単なるミスです(笑)。(20, 30, 20, 20)と指定すれば左右のpaddingが20ピクセルになります。
もしも2つのButtonの間にもスペースを空けたければ、「spacing : 10」などのように記述を追加すればOKです。

さらにそれぞれのButtonウィジェットにおいて、on_press によってボタンが押されたときに呼び出される関数を指定しています。ここで「root」とはImageViewerクラスを指します。
さいごに
これまでいくつかPythonのGUIプログラムを紹介しましたが、いずれもtkinterというGUIライブラリを使ったものでした。今回使ったKivyを使えばiOSやAndroidのプログラムもPythonで作れるというので、魅力を感じています。私も勉強し始めたばかりであまり詳しいことは分かっていませんが、これから勉強していきたいと思っています。今回の内容が誰かのお役に立てば幸いです。
- 投稿タグ
- プログラミング