kivy

kivy戦記(16-10) json読み込みから、一通り動くか確認してみる

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

では、いよいよjson読み込みから、一通り動くか確認してみる。

json読み込みロジックを書いてみよう。

filelist.py(余計なロジックやimportは、外しています。)


import os
import sys
import json
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
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()
path_inf = 'path_inf.json'
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):
    begin_path_dsp = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(SetSCN, self).__init__(**kwargs)
    def setSelect(self):
        self._popup = LstPOP(   load = self.setSelectLoad, \
                                cancel = self.setSelectCancel)
        self._popup.open()
    def setSelectLoad (self, path, filename):
        self.begin_path_dsp.text = path
        self._popup.dismiss()
    def setSelectCancel(self):
        self._popup.dismiss()
    def msgTestOk(self):
        pprint ('OKが押されました')
        self.popup.dismiss()
    def msgTestCancel(self):
        pprint ('CANCELが押されました')
        self.popup.dismiss()
    def selfOk(self):
        self.bl = {}
        self.bl['begin_path'] = self.begin_path_dsp.text
        pprint (self.bl)
        try:
            #raise OverflowError('やっちまったな')
            with open(path_inf, 'w') as fd:
                json.dump(self.bl, fd)
        except PermissionError as err:
            self.msg = '保存データを書き込もうとしましたが、書き込み許可がありません。\n'
            self.msg += 'パスは、' + self.bl['begin_path'] + 'です。\n'
            self.msg += '保存先の書き込み許可を確認するか、パスが適当かを確認してください。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='書き込み許可エラー', content=content)
            self.popup.open()
        except IOError as err:
            self.msg = '保存データを書き込もうとしましたが、書き込めません。\n'
            self.msg += '書き込み先機器に問題があるようです。\n'
            self.msg += '書き込み先機器の状態を確認してください。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='I/Oエラー', content=content)
            self.popup.open()
        except BaseException as err:
            self.msg = '保存データを書き込もうとしましたが、何らかのエラーが起きました。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='その他のエラー', content=content)
            self.popup.open()
        finally:
            sm.current = 'all'
    def selfCancel(self):
        sm.current = 'all'
class LstPOP(Popup):
    current_dir = os.path.dirname(os.path.abspath(__file__))
    load = ObjectProperty(None)
    cancel = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(LstPOP, self).__init__(**kwargs)
        self.title ='読み込み中 ' + os.path.dirname(os.path.abspath(__file__))
    def is_dir(self, dirname, filename):
        # ディスレクトリならTrueを返す
        return os.path.isdir(os.path.join(dirname, filename))
    def lstChooser(self, path, selection):
        if 0 < len(selection):
            self.title = '読み込み中 ' + selection[0]
class AllSCN(Screen):
    tv = ObjectProperty(None)
    file_name = ObjectProperty(None)
    loadfile = ObjectProperty(None)
    text_input = ObjectProperty(None)
    def msgOsErrorOk(self):
        print('OKが押されました')
        self.popup.dismiss()
        sys.exit()
    def begin_display(self, dt):
        #
        # SetSCNの最初期内容
        #
        path = ''
        thisos = os.name
        if thisos == 'posix':
            path = os.environ.get('HOME')
        elif thisos == 'nt':
            path = os.environ.get('HOMEDRIVE') + os.environ.get('HOMEPATH')
        else:
            msg = '内部OS判定(os.name)が、"' + thisos + '”でした。\n'
            msg += 'サポートしているOSは、UNIX系統と、Windowsです。\n'
            msg += 'したがってこのOSはサポート外です。すみません。'
            content = MsgboxOk \
                (ok_button= self.msgOsErrorOk, \
                message_text= msg)
            self.popup = Popup(title='OSサポートエラー', content=content)
            self.popup.open()
            return
        self.manager.get_screen('set').begin_path_dsp.text = path
        #
        # AllSCNのtree処理
        #
        self.tv.add_node(TreeViewLabel(text ='シンカリオン E1 とき'))
        self.tv.add_node(TreeViewLabel(text ='シンカリオン 0 ひかり'))
        self.tv.add_node(TreeViewLabel(text ='シンカリオン キハ32 鉄道ホビートレイン'))
    def setupButtonClicked(self):
        #
        # SetSCNへの画面遷移時の内容
        #
        try:
            #raise OverflowError('やっちまったな')
            with open(path_inf, 'r') as fd:
                path_json = json.load(fd)
                self.manager.get_screen('set').begin_path_dsp.text = path_json['begin_path']
        except PermissionError as err:
            self.msg = '保存データを読み込もうとしましたが、読み込み許可がありません。\n'
            self.msg += 'パスは、' + self.bl['begin_path'] + 'です。\n'
            self.msg += '保存先の読み込み許可を確認するか、パスが適当かを確認してください。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='読み込み許可エラー', content=content)
            self.popup.open()
        except IOError as err:
            self.msg = '保存データを読み込もうとしましたが、読み込めません。\n'
            self.msg += '読み込み先機器に問題があるようです。\n'
            self.msg += '読み込み先機器の状態を確認してください。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='I/Oエラー', content=content)
            self.popup.open()
        except BaseException as err:
            self.msg = '保存データを読み込もうとしましたが、何らかのエラーが起きました。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='その他のエラー', content=content)
            self.popup.open()
        else:
            sm.current = 'set'

    def show_load(self):
        self._popup = LstPOP(   load = self.load, \
                                cancel = self.dismiss_popup)
        self._popup.open()
    def load (self, path, filename):
        pprint(filename)
        #self.file_name.text = filename[0]
        self.file_name.text = path
        #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()
    def msgTestOk(self):
        pprint ('OKが押されました')
        self.popup.dismiss()
    def msgTestCancel(self):
        pprint ('CANCELが押されました')
        self.popup.dismiss()

class FilelistApp(App):
    def build(self):
        allscn = AllSCN()
        setscn = SetSCN()
        Clock.schedule_once(allscn.begin_display, 0)
        sm.add_widget(allscn)
        sm.add_widget(setscn)
        return sm
if __name__ == '__main__':
    FilelistApp().run()

 

filelist.kvは、変更なし。

 

SetSCNの初期処理を、AllSCN側に入れなければならないのは釈然としないが、ScreenManagerを使用しているときは、どちらも同じ画面になっているという思想である。

これしかしようがないが、なんだか釈然としない。

 

で、動かしてみよう。(path_inf.jsonは、あらかじめ変更しています)

「設定」押下で、

「(なんたら)/左巻きなると」というのは、もともとpath_inf.jsonの値で実在するパス。

で、「・・・」押下で、このパスに行けばいいのだが、、、、

ああ、苦労してパス表示をつくって良かった、、、、、は、いいとして、左巻きなるとのパスに移ってない。

これは困った、対処しなければ。

 

そんなわけで、対処しました。

filelist.py


import os
import sys
import json
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
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()
path_inf = 'path_inf.json'
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):
    begin_path_dsp = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(SetSCN, self).__init__(**kwargs)
    def setSelect(self):
        self._popup = LstPOP(   load = self.setSelectLoad, \
                                cancel = self.setSelectCancel , \
                                start_path = self.begin_path_dsp.text)
        self._popup.open()
    def setSelectLoad (self, path, filename):
        self.begin_path_dsp.text = path
        self._popup.dismiss()
    def setSelectCancel(self):
        self._popup.dismiss()
    def msgTestOk(self):
        pprint ('OKが押されました')
        self.popup.dismiss()
    def msgTestCancel(self):
        pprint ('CANCELが押されました')
        self.popup.dismiss()
    def selfOk(self):
        self.bl = {}
        self.bl['begin_path'] = self.begin_path_dsp.text
        pprint (self.bl)
        try:
            #raise OverflowError('やっちまったな')
            with open(path_inf, 'w') as fd:
                json.dump(self.bl, fd)
        except PermissionError as err:
            self.msg = '保存データを書き込もうとしましたが、書き込み許可がありません。\n'
            self.msg += 'パスは、' + self.bl['begin_path'] + 'です。\n'
            self.msg += '保存先の書き込み許可を確認するか、パスが適当かを確認してください。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='書き込み許可エラー', content=content)
            self.popup.open()
        except IOError as err:
            self.msg = '保存データを書き込もうとしましたが、書き込めません。\n'
            self.msg += '書き込み先機器に問題があるようです。\n'
            self.msg += '書き込み先機器の状態を確認してください。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='I/Oエラー', content=content)
            self.popup.open()
        except BaseException as err:
            self.msg = '保存データを書き込もうとしましたが、何らかのエラーが起きました。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='その他のエラー', content=content)
            self.popup.open()
        finally:
            sm.current = 'all'
    def selfCancel(self):
        sm.current = 'all'
class LstPOP(Popup):
    #current_dir = os.path.dirname(os.path.abspath(__file__))

    load = ObjectProperty(None)
    cancel = ObjectProperty(None)
    start_path = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(LstPOP, self).__init__(**kwargs)
        self.title ='読み込み中 ' + self.start_path
    def is_dir(self, dirname, filename):
        # ディスレクトリならTrueを返す
        return os.path.isdir(os.path.join(dirname, filename))
    def lstChooser(self, path, selection):
        if 0 < len(selection):
            self.title = '読み込み中 ' + selection[0]
class AllSCN(Screen):
    tv = ObjectProperty(None)
    file_name = ObjectProperty(None)
    loadfile = ObjectProperty(None)
    text_input = ObjectProperty(None)
    def msgOsErrorOk(self):
        print('OKが押されました')
        self.popup.dismiss()
        sys.exit()
    def begin_display(self, dt):
        #
        # SetSCNの最初期内容
        #
        path = ''
        thisos = os.name
        if thisos == 'posix':
            path = os.environ.get('HOME')
        elif thisos == 'nt':
            path = os.environ.get('HOMEDRIVE') + os.environ.get('HOMEPATH')
        else:
            msg = '内部OS判定(os.name)が、"' + thisos + '”でした。\n'
            msg += 'サポートしているOSは、UNIX系統と、Windowsです。\n'
            msg += 'したがってこのOSはサポート外です。すみません。'
            content = MsgboxOk \
                (ok_button= self.msgOsErrorOk, \
                message_text= msg)
            self.popup = Popup(title='OSサポートエラー', content=content)
            self.popup.open()
            return
        self.manager.get_screen('set').begin_path_dsp.text = path
        #
        # AllSCNのtree処理
        #
        self.tv.add_node(TreeViewLabel(text ='シンカリオン E1 とき'))
        self.tv.add_node(TreeViewLabel(text ='シンカリオン 0 ひかり'))
        self.tv.add_node(TreeViewLabel(text ='シンカリオン キハ32 鉄道ホビートレイン'))
    def setupButtonClicked(self):
        #
        # SetSCNへの画面遷移時の内容
        #
        try:
            #raise OverflowError('やっちまったな')
            with open(path_inf, 'r') as fd:
                path_json = json.load(fd)
                self.manager.get_screen('set').begin_path_dsp.text = path_json['begin_path']
        except PermissionError as err:
            self.msg = '保存データを読み込もうとしましたが、読み込み許可がありません。\n'
            self.msg += 'パスは、' + self.bl['begin_path'] + 'です。\n'
            self.msg += '保存先の読み込み許可を確認するか、パスが適当かを確認してください。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='読み込み許可エラー', content=content)
            self.popup.open()
        except IOError as err:
            self.msg = '保存データを読み込もうとしましたが、読み込めません。\n'
            self.msg += '読み込み先機器に問題があるようです。\n'
            self.msg += '読み込み先機器の状態を確認してください。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='I/Oエラー', content=content)
            self.popup.open()
        except BaseException as err:
            self.msg = '保存データを読み込もうとしましたが、何らかのエラーが起きました。\n\n'
            self.msg += str(err)
            content = MsgboxOk \
                (ok_button= self.msgTestOk, \
                message_text= self.msg)
            self.popup = Popup(title='その他のエラー', content=content)
            self.popup.open()
        else:
            sm.current = 'set'
    def show_load(self):
        # 暫定ロジックです
        self._popup = LstPOP(   load = self.load, \
                                cancel = self.dismiss_popup, \
                                start_path='あああ')
        self._popup.open()
    def load (self, path, filename):
        pprint(filename)
        #self.file_name.text = filename[0]
        self.file_name.text = path
        #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()
    def msgTestOk(self):
        pprint ('OKが押されました')
        self.popup.dismiss()
    def msgTestCancel(self):
        pprint ('CANCELが押されました')
        self.popup.dismiss()
class FilelistApp(App):
    def build(self):
        allscn = AllSCN()
        setscn = SetSCN()
        Clock.schedule_once(allscn.begin_display, 0)
        sm.add_widget(allscn)
        sm.add_widget(setscn)
        return sm
if __name__ == '__main__':
    FilelistApp().run()

 

filelist.kv


<SetSCN>:
    name: 'set'
    begin_path_dsp: begin_path_dsp
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            size_hint_y: 0.1
            orientation: 'horizontal'
            BoxLayout:
                Label:
                    size_hint_x: 0.1
                    text: '開始パス'
                TextInput:
                    id: begin_path_dsp
                    size_hint_x: 0.8
                Button:
                    size_hint_x: 0.1
                    text: '…'
                    on_release: root.setSelect()
        BoxLayout:
            size_hint_y: 0.8
        BoxLayout:
            size_hint_y: 0.1
            Button:
                size_hint_x: 0.5
                text: 'OK'
                on_release: root.selfOk()
            Button:
                size_hint_x: 0.5
                text: 'キャンセル'
                on_release: root.selfCancel()
<AllSCN>:
    name: 'all'
    tv: tv
    file_name: file_name
    BoxLayout:
        orientation: 'vertical'
        BoxLayout: #OpnSCN
            size_hint_y: 0.1
            orientation: 'horizontal'
            BoxLayout:
                Button:
                    size_hint_x: 0.1
                    text: '設定'
                    on_release: root.setupButtonClicked()
                TextInput:
                    id: file_name
                    size_hint_x: 0.7
                    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
<MsgboxOk>
    orientation: 'vertical'
    Label:
        size_hint_y: 0.9
        text: root.message_text
    BoxLayout:
        size_hint_y: 0.1
        orientation: 'horizontal'
        Button:
            size_hint_x: 10
            text: 'OK'
            on_release: root.ok_button()
<MsgboxOkCancel>
    orientation: 'vertical'
    Label:
        size_hint_y: 0.9
        text: root.message_text
    BoxLayout:
        size_hint_y: 0.1
        orientation: 'horizontal'
        Button:
            size_hint_x: 10
            text: 'OK'
            on_release: root.ok_button()
        Button:
            size_hint_x: 10
            text: 'キャンセル'
            on_release: root.cancel_button()
<LstPOP>:
    id:lst_pop
    size_hint: 0.9, 0.9
    #title: '読み込み中 ' + root.start_path
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: 'vertical'
        FileChooserListView:
            id: filechooser
            dirselect: True
            path: root.start_path
            filters: [root.is_dir]
            on_touch_down: lst_pop.lstChooser(filechooser.path, filechooser.selection)
        BoxLayout:
            size_hint_y : None
            height : 30
            Button:
                text: 'キャンセル'
                on_release: root.cancel()
            Button:
                text: '読み込み'
                on_release: root.load(filechooser.path, filechooser.selection)
AllSCN:

まず、クラスLstPOPに、外部からパス設定できるように、start_pathを新設。

初期表示で表示できるようにする。

filelist.py


class LstPOP(Popup):
    #current_dir = os.path.dirname(os.path.abspath(__file__))

    load = ObjectProperty(None)
    cancel = ObjectProperty(None)
    start_path = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(LstPOP, self).__init__(**kwargs)
        self.title ='読み込み中 ' + self.start_path

    def is_dir(self, dirname, filename):
        # ディスレクトリならTrueを返す
        return os.path.isdir(os.path.join(dirname, filename))
    def lstChooser(self, path, selection):
        if 0 < len(selection):
            self.title = '読み込み中 ' + selection[0]

 

filelist.kv側の<LstPOP>FileChooserListView:のpath指定はroot.start_pathに変更。


<LstPOP>:
    id:lst_pop
    size_hint: 0.9, 0.9
    #title: '読み込み中 ' + root.start_path
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: 'vertical'
        FileChooserListView:
            id: filechooser
            dirselect: True
            path: root.start_path
            filters: [root.is_dir]
            on_touch_down: lst_pop.lstChooser(filechooser.path, filechooser.selection)

 

呼び出し側では、そのstart_pathに初期パスのtextフィールドを設定する。

filelist.py


class SetSCN(Screen):
    begin_path_dsp = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(SetSCN, self).__init__(**kwargs)
    def setSelect(self):
        self._popup = LstPOP(   load = self.setSelectLoad, \
                                cancel = self.setSelectCancel , \
                                start_path = self.begin_path_dsp.text)
        self._popup.open()

 

では実行。その前に、最初期の操作という設定で、jsonファイルpath_inf.jsonを消しておく。

では実行。

ああ、やっちまった、、、、

では修正。

 

「設定」ボタンを押されたときに、jsonファイルが読み込まれるが、ここにファイルがないときの処理を新設する。

そしてファイルがないときには、AllSCN()のbegin_display()で求めた、jsonファイルがないときのOS別の初期値をSetSCNのパスとして表示させるのだ。

そのため、AllSCN()のbegin_display()で使っている変数pathは、self.init_pathに変更し、この下にあるSetSCNのパス表示の初期設定を廃止するのだ。

 

filelist.py

(省略ですわよ)
class AllSCN(Screen):
    tv = ObjectProperty(None)
    file_name = ObjectProperty(None)
    loadfile = ObjectProperty(None)
    text_input = ObjectProperty(None)
    def msgOsErrorOk(self):
        print('OKが押されました')
        self.popup.dismiss()
        sys.exit()
    def begin_display(self, dt):
        #
        # SetSCNの最初期内容
        #
        self.init_path = ''
        thisos = os.name
        if thisos == 'posix':
            self.init_path = os.environ.get('HOME')
        elif thisos == 'nt':
            self.init_path = os.environ.get('HOMEDRIVE') + os.environ.get('HOMEPATH')
        else:
            msg = '内部OS判定(os.name)が、"' + thisos + '”でした。\n'
            msg += 'サポートしているOSは、UNIX系統と、Windowsです。\n'
            msg += 'したがってこのOSはサポート外です。すみません。'
            content = MsgboxOk \
                (ok_button= self.msgOsErrorOk, \
                message_text= msg)
            self.popup = Popup(title='OSサポートエラー', content=content)
            self.popup.open()
            return
        #self.manager.get_screen('set').begin_path_dsp.text = self.init_path

(省略ですってよ) def setupButtonClicked(self): # # SetSCNへの画面遷移時の内容 # try: #raise OverflowError('やっちまったな') with open(path_inf, 'r') as fd: path_json = json.load(fd) self.manager.get_screen('set').begin_path_dsp.text = path_json['begin_path'] except FileNotFoundError as err: self.manager.get_screen('set').begin_path_dsp.text = self.init_path sm.current = 'set'
(以下省略しますわよ)

 

これで実行しよう。起動したら「設定」ボタンで、、、

 

「左巻きなると」ディレクトリに移動。

 

「左巻きなると」パスが表示された。

これで、「読み込み」。

「OK」の後に、このfilelistを一旦終了。

そして再度起動、「設定」を押して、

変更したパスが、再度表示される。

これで初期表示画面ができた。

 

と、言いたいところだが、一つ罠があった。

それは、「開始パス」フィールドを手入力可能にしていたことだった。

変な値を入れると、変に動く。

どうしたらいいんだっ!

(続く)

 

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