kivy

kivy戦記(19-2) TreeList処理

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

では、実装する。

 

kv19c.py


import os
import sys
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.treeview import TreeView, TreeViewLabel
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty, StringProperty
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()
Builder.load_string('''
<ResultParts>:
    tv_p: tv_v
    BoxLayout:
        ScrollView:
            #size: self.size
            do_scroll_x: False
            TreeView:
                id: tv_v
                root_options: {'text':'ホーム', 'font_size': 32}
                hide_root: False
                size_hint_y: None
''')
class ResultParts(BoxLayout):
    start_path = StringProperty(None)
    tv_p = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(ResultParts, self).__init__(**kwargs)
        self.start_path = kwargs['start_path']
        self.tv_p.bind(minimum_height = self.tv_p.setter('height'))
        self.tree_height = self.idv_dir(self.start_path, None)
    def idv_dir(self, this_dir, parent):
        try:
            #raise BaseException('やっちまったな')
            print('this_dir:', this_dir)
            ls = os.listdir(path=this_dir)
            # 指定ディスレクトリ内のファイル・ディレクトリを一度フルパスにしてから、ディレクトリであればリストに入れる
            ls_dir = [ls_idv for ls_idv in ls if os.path.isdir(os.path.join(this_dir, ls_idv))]
            ls_dir.sort()
            # 指定ディスレクトリ内のファイル・ディレクトリを一度フルパスにしてから、ファイルであればリストに入れる
            ls_file = [ls_idv for ls_idv in ls if os.path.isfile(os.path.join(this_dir, ls_idv))]
            ls_file.sort()
            for ls_dir_idv in ls_dir:
                tree_node = self.tv_p.add_node(TreeViewLabel(text=ls_dir_idv, is_open=False), parent)
                self.idv_dir(os.path.join(this_dir, ls_dir_idv), tree_node)
            if ls_file:
                for ls_file_idv in ls_file:
                    self.tv_p.add_node(TreeViewLabel(text=ls_file_idv, is_open=False), parent)
            else:
                # ディレクトリの中にファイルが1つも入っていない時の処理
                self.tv_p.add_node(TreeViewLabel(text='(empty)', is_open=False), parent)
        except PermissionError as err:
            pass
        except IOError as err:
            self.msg = 'ファイルデータを読み込もうとしましたが、読み込めません。\n'
            self.msg += '読み込み先機器に問題があるようです。\n'
            self.msg += '読み込み先機器の状態を確認してください。\n\n'
            self.msg += str(err)
            print ('Err Msg:', self.msg)
        except BaseException as err:
            self.msg = 'ファイルデータを読み込もうとしましたが、何らかのエラーが起きました。\n\n'
            self.msg += str(err)
            print ('Err Msg:', self.msg)
    def btn_ok(self):
        print('OK button')
        self.popup.dismiss()
class TestSCN(Screen):
    def __init__(self, **kwargs):
        super(TestSCN, self).__init__(**kwargs)
        #start_pathの中身は、お好きなパスに置き換えてください
        self.add_widget(ResultParts(start_path='(いやーん)'))
class TestApp(App):
    def build(self):
        sm.add_widget(TestSCN(name='test'))
        return sm
if __name__ == '__main__':
    TestApp().run()

 

実行例

 

 

 

まずこのソース、そのままコピペしても動かない。

今回自分の/home以下のディレクトリ・ファイルで確認したので、この部分をお使いの環境に合わせていただきたい。


class TestSCN(Screen):
    def __init__(self, **kwargs):
        super(TestSCN, self).__init__(**kwargs)
        #start_pathの中身は、お好きなパスに置き換えてください
        self.add_widget(ResultParts(start_path='(いやーん)'))

そして呼び出しているResultPartsなのですが、idv_dirという実際の中身の関数を、ディレクトリの深い部分を呼ぶため、再帰呼び出ししている。


class ResultParts(BoxLayout):
    start_path = StringProperty(None)
    tv_p = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(ResultParts, self).__init__(**kwargs)
        self.start_path = kwargs['start_path']
        self.tv_p.bind(minimum_height = self.tv_p.setter('height'))
        self.tree_height = self.idv_dir(self.start_path, None)

 

さて、ResultPartsはパラメータstart_pathを呼んでいるのだが、これをこのロジックで呼ぶとこんなエラーメッセージが返ってきた。


class ResultParts(BoxLayout):
    tv_p = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(ResultParts, self).__init__(**kwargs)
        self.start_path = kwargs['start_path']
        self.tv_p.bind(minimum_height = self.tv_p.setter('height'))
        self.tree_height = self.idv_dir(self.start_path, None)

 Traceback (most recent call last):
   File "/home/*******/MyApp/01_filelist/kv19c.py", line 120, in 
     TestApp().run()
   File "/usr/lib/python3/dist-packages/kivy/app.py", line 800, in run
     root = self.build()
   File "/home/*******/MyApp/01_filelist/kv19c.py", line 114, in build
     sm.add_widget(TestSCN(name='test'))
   File "/home/*******/MyApp/01_filelist/kv19c.py", line 108, in __init__
     self.add_widget(ResultParts(start_path='/home/*******/MyApp/01_filelist_testdata'))
   File "/home/*******/MyApp/01_filelist/kv19c.py", line 44, in __init__
     super(ResultParts, self).__init__(**kwargs)
   File "/usr/lib/python3/dist-packages/kivy/uix/boxlayout.py", line 131, in __init__
     super(BoxLayout, self).__init__(**kwargs)
   File "/usr/lib/python3/dist-packages/kivy/uix/layout.py", line 76, in __init__
     super(Layout, self).__init__(**kwargs)
   File "/usr/lib/python3/dist-packages/kivy/uix/widget.py", line 340, in __init__
     super(Widget, self).__init__(**kwargs)
   File "kivy/_event.pyx", line 243, in kivy._event.EventDispatcher.__init__ (kivy/_event.c:4923)
 TypeError: object.__init__() takes no parameters
プロセスは終了コード 1 で完了しました

 

いろいろ悩んだが、仮にこうすることで一応解決した。


class ResultParts(BoxLayout):
    start_path = StringProperty(None)
    tv_p = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(ResultParts, self).__init__(**kwargs)
        self.start_path = kwargs['start_path']
        self.tv_p.bind(minimum_height = self.tv_p.setter('height'))
        self.tree_height = self.idv_dir(self.start_path, None)

あくまでも暫定的な処置ではある。

しかしのちに、filelist.pyに反映するときに問題が起きたが、これは後ほど(実は、これを執筆している時点でも解決していない!)

 

さて、その次の行。

これは、スクロールの高さ指定で、ここを参考にした。

(TreeView in a ScrollView in Kivy – No Scroll (stack overflow)

https://stackoverflow.com/questions/16122664/treeview-in-a-scrollview-in-kivy-no-scroll)


class ResultParts(BoxLayout):
    start_path = StringProperty(None)
    tv_p = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(ResultParts, self).__init__(**kwargs)
        self.start_path = kwargs['start_path']
        self.tv_p.bind(minimum_height = self.tv_p.setter('height'))
        self.tree_height = self.idv_dir(self.start_path, None)

 

さて、関数idv_dirの中身。

ディレクトリの中にファイルが何も入っていないときに、ファイルと同じ表示になってしまう問題が発生した。

下記の例では、ディレクトリaaが、空のディレクトリである。

そのため、ディレクトリの中にファイルが何も入っていないときに「(empty)」と入れている。


    def idv_dir(self, this_dir, parent):
        try:
            (省略ですわ)
            if ls_file:
                for ls_file_idv in ls_file:
                    self.tv_p.add_node(TreeViewLabel(text=ls_file_idv, is_open=False), parent)
            else:
                # ディレクトリの中にファイルが1つも入っていない時の処理
                self.tv_p.add_node(TreeViewLabel(text='(empty)', is_open=False), parent)

 

 

さて、今回のロジック。

実は、エラー処理のところで表示がずれてしまうのだが、「正式ロジックじゃないのでいいや」と思って大きな修正もかけずに、filelist.pyへマージしたが、思わぬ問題が発生したのだった。

(続く)

 

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