kivy

kivy戦記(16-3) 初期表示をしたい場合(追記あり)

kivy
この記事は約15分で読めます。


メッセージが2つ出る問題。
これは、「kivy戦記(15-2) 荒ぶるScreenManager (https://koh-sen.jp/blog/archives/7287)」で回避した、__init__が2回動いている問題に他ならない。
ScreenManagerの問題と切り分けるために、実験的にこんなソースを用意した。
それは、ScreenManagerを入れる前の「kivy戦記(11) ファイル選択 (https://koh-sen.jp/blog/archives/7187)」のソースに、OS判定のロジックを追加したものである。
これを、kv11a.py、kv11a.kvとする。
kv11a.py


import os
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.uix.treeview import TreeViewLabel
from kivy.properties import ObjectProperty
from pprint import pprint
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.resources import resource_add_path
# 日本語フォント設定
resource_add_path('./fonts')
LabelBase.register(DEFAULT_FONT, 'ipaexg.ttf')
class LoadDialog(FloatLayout):
    load = ObjectProperty(None)
    cancel = ObjectProperty(None)
class AllSCN(BoxLayout):
    tv = ObjectProperty(None)
    loadfile = ObjectProperty(None)
    text_input = ObjectProperty(None)
    def __init__(self, **kwargs):
         super(AllSCN, self).__init__(**kwargs)
         thisos = os.name
         if thisos == 'posix':
             print('UNIX系統が動いています')
         elif thisos == 'nt':
             print('Windowsが動いています')
         else:
             print('サポートしていないOSが動いています')
         self.tv.add_node(TreeViewLabel(text ='シンカリオン E1 とき'))
         self.tv.add_node(TreeViewLabel(text ='シンカリオン 0 ひかり'))
         self.tv.add_node(TreeViewLabel(text ='シンカリオン キハ32 鉄道ホビートレイン'))
    def show_load(self):
        content = LoadDialog(load = self.load, cancel = self.dismiss_popup)
        self._popup = Popup( title="読み込み中", content=content, size_hint=(0.9,0.9))
        self._popup.open()
    def load (self, path, filename):
        pprint(filename)
        #with open (os.path.join(path, filename[0])) as stream:
        #    self.text_input.text = stream.read()
        self.dismiss_popup()
    def dismiss_popup(self):
        self._popup.dismiss()
class Kv11aApp(App):
    def build(self):
        return AllSCN()
if __name__ == '__main__':
    Kv11aApp().run()

kv11a.kv


<AllSCN>:
    tv: tv
    orientation: 'vertical'
    BoxLayout: #OpnSCN
        size_hint_y: 0.1
        TextInput:
            size_hint_x: 0.8
            text: 'ファイルパス'
        Button:
            size_hint_x: 0.1
            text: '…'
            on_release: root.show_load()
        Button:
            size_hint_x: 0.1
            text: '実行'
    BoxLayout: #ResultSCN
        size_hint_y: 0.9
        TreeView:
            id: tv
<LoadDialog>:
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: 'vertical'
        FileChooserListView:
            id: filechooser
        BoxLayout:
            size_hint_y : None
            height : 30
            Button:
                text: 'キャンセル'
                on_release: root.cancel()
            Button:
                text: '読み込み'
                on_release: root.load(filechooser.path, filechooser.selection)
AllSCN:

実行すると、

同じ問題が出たから、ScreenManagerそのものはシロである。
では、どういう順序で動いているか、確認してみよう。
次の部分にブレークポイントを入れる。
kv11a.py

そして、「kivy戦記(13-4) ObjectProperty(None)と禁断の技(https://koh-sen.jp/blog/archives/7256)」で、kivy内部を覗いた成果である、builder.pyでparser(kvファイルデータの内部表現)を作ろうとしている所にも、ブレークポイントを入れる。
builder.py

その結果がこれ。

①で、なぜかbuilder.pyが先に動いているが、これはkivyの初期動作なのか、全く別のkvファイルを読み込むという動作をしている。
したがって、基本的に無視。
②で、run()。これで処理の実行を行う。ここでkivyファイルを読み込んだりする。
③で、再びbuilder.py。kv11a.kvをチェックし内部表現にしている。
④の前の文で、最初のprint
⑤で、AllSCN()をインスタンス生成。
ここで、再びAllSCN()の__init__()が実行されてしまうので、
⑥が実行されて、2度目のprintが表示される。
kvファイル読み込みは不可避、またbuild()でのAllSCN()インスタンス化も不可避なので、⑥の状況ををヒントにして、改善をすればいいはずだ。
これは仮説だが、AllSCN()の__init__()が不要で、この代わりをkvファイルに書けば良いことになる。
しかし、これは初期表示の時点で、すでに計算されたものを表示するという仕様で作っているのだ。
現在、仮に「シンカリオン」となっている部分が、最終的にはツリー表示されるのだ。
これは譲れない。どうしたものか。
そうだ。京都、行こうClockを使おう。起動後0秒後の表示で初期表示をしよう。
早速ソースを修正する。
kv11a.py


import os
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.uix.treeview import TreeViewLabel
from kivy.properties import ObjectProperty
from kivy.clock import Clock
from pprint import pprint
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.resources import resource_add_path
# 日本語フォント設定
resource_add_path('./fonts')
LabelBase.register(DEFAULT_FONT, 'ipaexg.ttf')
class LoadDialog(FloatLayout):
    load = ObjectProperty(None)
    cancel = ObjectProperty(None)
class AllSCN(BoxLayout):
    tv = ObjectProperty(None)
    loadfile = ObjectProperty(None)
    text_input = ObjectProperty(None)
    def begin_display(self, dt):
         thisos = os.name
         if thisos == 'posix':
             print('UNIX系統が動いています')
         elif thisos == 'nt':
             print('Windowsが動いています')
         else:
             print('サポートしていないOSが動いています')
         self.tv.add_node(TreeViewLabel(text ='シンカリオン E1 とき'))
         self.tv.add_node(TreeViewLabel(text ='シンカリオン 0 ひかり'))
         self.tv.add_node(TreeViewLabel(text ='シンカリオン キハ32 鉄道ホビートレイン'))
    def show_load(self):
        content = LoadDialog(load = self.load, cancel = self.dismiss_popup)
        self._popup = Popup( title="読み込み中", content=content, size_hint=(0.9,0.9))
        self._popup.open()
    def load (self, path, filename):
        pprint(filename)
        #with open (os.path.join(path, filename[0])) as stream:
        #    self.text_input.text = stream.read()
        self.dismiss_popup()
    def dismiss_popup(self):
        self._popup.dismiss()
class Kv11aApp(App):
    def build(self):
        allscn = AllSCN()
        Clock.schedule_once(allscn.begin_display, 0)
        return allscn
if __name__ == '__main__':
    Kv11aApp().run()

kv11a.kvは変更なし。
それで起動しよう。
起動画面が、

コンソールの内容は、

これで良さそうである。
kivyの初期表示は、いろいろ探したけど見つからず、回避手段となったけど、これが正しいみたいだ。
では本来のソースfilelist.kvに反映しよう
filelist.py


import os
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.uix.treeview import TreeViewLabel
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
from pprint import pprint
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.resources import resource_add_path
# 日本語フォント設定
resource_add_path('./fonts')
LabelBase.register(DEFAULT_FONT, 'ipaexg.ttf')
sm = ScreenManager()
class MsgboxOk(BoxLayout):
    message_text = StringProperty()
    ok_button = ObjectProperty(None)
class MsgboxOkCancel(BoxLayout):
    message_text = StringProperty()
    ok_button = ObjectProperty(None)
    cancel_button = ObjectProperty(None)
class SetSCN(Screen):
    def __init__(self, **kwargs):
        super(SetSCN, self).__init__(**kwargs)
    def msgTestOk(self):
        pprint ('OKが押されました')
        self.popup.dismiss()
    def msgTestCancel(self):
        pprint ('CANCELが押されました')
        self.popup.dismiss()
    def selfOk(self):
        content = MsgboxOkCancel \
            (ok_button= self.msgTestOk, \
            cancel_button= self.msgTestCancel, \
            message_text= 'とりあえずボタンを作りましたよ')
        self.popup = Popup(title='ボタンだがや', content=content)
        self.popup.open()
        sm.current = 'all'
    def selfCancel(self):
        sm.current = 'all'
class LoadDialog(FloatLayout):
    load = ObjectProperty(None)
    cancel = ObjectProperty(None)
class AllSCN(Screen):
    tv = ObjectProperty(None)
    file_name = ObjectProperty(None)
    loadfile = ObjectProperty(None)
    text_input = ObjectProperty(None)
    def begin_display(self, dt):
        thisos = os.name
        if thisos == 'posix':
            print ('UNIX系統が動いています')
        elif thisos == 'nt':
            print ('Windowsが動いています')
        else:
            print ('サポートしていないOSが動いています')
        self.tv.add_node(TreeViewLabel(text ='シンカリオン E1 とき'))
        self.tv.add_node(TreeViewLabel(text ='シンカリオン 0 ひかり'))
        self.tv.add_node(TreeViewLabel(text ='シンカリオン キハ32 鉄道ホビートレイン'))
    def setupButtonClicked(self):
        sm.current = 'set'
    def show_load(self):
        content = LoadDialog(load = self.load, cancel = self.dismiss_popup)
        self._popup = Popup( title="読み込み中", content=content, size_hint=(0.9,0.9))
        self._popup.open()
    def load (self, path, filename):
        pprint(filename)
        self.file_name.text = filename[0]
        #with open (os.path.join(path, filename[0])) as stream:
        #    self.text_input.text = stream.read()
        self.dismiss_popup()
    def dismiss_popup(self):
        self._popup.dismiss()
class FilelistApp(App):
    def build(self):
        allscn = AllSCN(name='all')
        setscn = SetSCN(name='set')
        Clock.schedule_once(allscn.begin_display, 0)
        sm.add_widget(allscn)
        sm.add_widget(setscn)
        return sm
if __name__ == '__main__':
    FilelistApp().run()

filelist.kvは変更がない。
で、起動。

「設定」ボタン押下

「キャンセル」ボタン押下。このとき元の画面に戻っていることを確認。
このときのコンソールが。

寄り道をしたけど、この問題は解決した。たぶん。

タイトルとURLをコピーしました