先日(昨日)、PythonでGUIを作らなきゃいけなくなって調べてみたら、
Kivyってのがいいよ
的な記事を見つけたので、Kivyを使ってみることに。
画像の更新に苦しんだので、そのメモ。
固定画像を表示させたり、ボタンが押されたら画像を差し替えるのは見つかったが、
プログラム上で、あるタイミングで切り替えるってのがなかなかできなかった&見つからなかった。
画像の表示
これは、簡単。こんな感じ。
from kivy.app import App
from kivy.properties import StringProperty
from kivy.uix.widget import Widget
class ImageWidget(Widget):
image_src = StringProperty('')
def __init__(self, **kwargs):
super(ImageWidget, self).__init__(**kwargs)
self.image_src = './image.png'
pass
class ImageApp(App):
def __init__(self, **kwargs):
super(ImageApp, self).__init__(**kwargs)
self.title = 'Image Slide'
def build(self):
return ImageWidget()
if __name__ == '__main__':
app = ImageApp()
app.run()
#:kivy 1.10
<ImageWidget>:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size: root.size
orientation: 'vertical'
Label:
text: root.image_src
color: 0, 0, 0, 1
font_size: 20
size_hint_y: 0.1
Image:
size_hint_y: 0.9
allow_stretch: True
source: root.image_src
これを実行すると、画像とその上に画像ファイル名が表示されるGUIが出る。
画像の更新
ここで、プログラム上で表示する画像を変更するようなアプリケーションにしたいとする。
たとえば、
あるディレクトリ内にある画像を順番にスライドショーするようなアプリケーション
最初のソース (問題点あり)
上記のアプリケーションを実現しようとしてこんな感じのソースを書いた。
from kivy.app import App
from kivy.properties import StringProperty
from kivy.uix.widget import Widget
import os
import glob
import random
import threading
from time import sleep
class ImageSlideWidget(Widget):
image_src = StringProperty('')
def __init__(self, **kwargs):
super(ImageSlideWidget, self).__init__(**kwargs)
self.image_src = './image.png'
pass
def set_image_src(self, src):
self.image_src = src
class ImageSlideApp(App):
def __init__(self, **kwargs):
super(ImageSlideApp, self).__init__(**kwargs)
self.title = 'Image Slide'
def build(self):
self.widget = ImageSlideWidget()
return self.widget
def set_image_src(self, src):
self.widget.set_image_src(src)
def slides(app, a):
dir_name = "./images"
files = glob.glob(os.path.join(dir_name, "*.png"))
while True:
random.shuffle(files)
for file_name in files:
sleep(1)
app.set_image_src(file_name)
def app_run(app, a):
app.run()
if __name__ == '__main__':
app = ImageSlideApp()
th_app = threading.Thread(target=app_run, args=(app, 0), daemon=True)
th_app.start()
sleep(1)
th_slide = threading.Thread(target=slides, args=(app, 0), daemon=True)
th_slide.start()
input('Pless any key.\n')
~ image.kvとクラス名以外同じなので省略 ~
とりあえず、Threadの使い方や、構造がいいかどうかは置いておいて、これで動きそうかなという感じで書いてみたが、
最初に、./image.png
が表示され、1秒ごとに、画像ファイル名は、更新されるようになった。
しかし、画像はちゃんと更新されず、真っ黒になった。
(おそらく、ブランク画像。)
次のソース (問題点あり)
Imageのsourceを入れ替えただけでは、画像のロードが行われないのだろう
と思い、調べたところ、
Imageが、reload()を持っていることがわかったので、下のように修正してみた。
from kivy.app import App
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.widget import Widget
import os
import glob
import random
import threading
from time import sleep
class ImageSlideWidget(Widget):
image = ObjectProperty(None)
image_src = StringProperty('')
def __init__(self, **kwargs):
super(ImageSlideWidget, self).__init__(**kwargs)
self.image_src = './image.png'
pass
def set_image_src(self, src):
self.image_src = src
self.image.reload()
~ 以下、上記ImageSlideApp_1.pyと同じなので省略 ~
#:kivy 1.10
<ImageSlideWidget>:
image: image
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size: root.size
orientation: 'vertical'
Label:
text: root.image_src
color: 0, 0, 0, 1
font_size: 20
size_hint_y: 0.1
Image:
id: image
size_hint_y: 0.9
allow_stretch: True
source: root.image_src
が、上記と変わらず、画像ファイル名は更新されるが、画像箇所は更新されず、真っ黒になった。
最終的なソース
色々さがしていたら、
Clock.schedule_interval()
というスケジューラ(?)があって、ここで、更新処理とかする
というのを見つけたので、
以下のように修正。
from kivy.app import App
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.widget import Widget
from kivy.clock import Clock
import os
import glob
import random
import threading
from time import sleep
class ImageSlideWidget(Widget):
image = ObjectProperty(None)
image_src = StringProperty('')
def __init__(self, **kwargs):
super(ImageSlideWidget, self).__init__(**kwargs)
self.image_src = './image.png'
Clock.schedule_interval(self.update, 0.01)
pass
def update(self, dt):
self.image.reload()
def set_image_src(self, src):
self.image_src = src
~ 以下、上記ImageSlideApp_1.py, ImageSlideApp_2.pyと同じなので省略 ~
~ 同じなので省略 ~
これで、1秒毎に、画像ファイル名と画像が更新されるようになりました。
まとめ
- 画像のソースを変えたら
Image.reload()
する。 -
Image.reload()
は、Clock.schedule_interval()
とかで、実行する。
[05/24 追記]
ここで紹介してるのは、sourceを使用した場合のImageの更新方法。
texureを使用した場合、この方法ではダメな様子。
備考
- 全体的に構造が良くないのは、すみません。
- もっといい方法知ってる方いらっしゃいましたら、ご享受ください。
-
すごくわかりやすかったです。ありがとうございます。@dario_okazakiさん ↩