最近このブログが「メイプルタウン物語」なるアニメの感想ばかりになっていて、kivyについては遂に投げ出してしまったのではないか、とお思いの皆様。

実は、前回のサンプルプログラムがそこそこうまくいったので、スキップスキップランランランと、次に取りかかったのですが、思わぬ罠が潜んでいて、それを脱するのに時間がかかってしまいました。

でも今は解決したので、スキップスキップランランラン的な気分です。


前回はRecyecleViewのサンプルプログラムがうまく行ったので、いよいよfilelist.pyに反映・・・と生きたいところですが、filelist.pyもfilelist.kvも作り込んでいくうちにどんどんステップ数が膨らんでいって、読まれている高も(そして私自身も)うんざりしていることでしょう。

そこで、「・・・」ボタンが押されて時の処理、つまりLstPOPの機能だけ抜粋して独立させて、テストしました。

 

kv18_3c.py


import os
import sys

from kivy.app import App
from kivy.lang import Builder
from kivy.utils import platform

from pprint import pprint

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen

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('''
<LstPOPDriveBtn>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            size: self.size
            pos: self.pos
    value: ''
    Button:
        text: root.value
        background_normal: ''
        background_color: 0.5, 0.5, 0.75, 1
        color: 1, 1 ,1 ,1
        on_press: root.on_select_button(self)

<LstPOP>:
    id:lst_pop
    size_hint: 0.9, 0.9
    drive_list: drive_list
    auto_dismiss: False
        
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: 'vertical'
        BoxLayout:
            orientation: 'horizontal'

            RecycleView:
                id: drive_list
                size_hint_x: 0.3
                scroll_type: ['bars', 'content']
                scroll_wheel_distance: sp(30) #スクロール速度
                bar_width: sp(10)
                viewclass: 'LstPOPDriveBtn'
                RecycleBoxLayout:
                    default_size: None, sp(40)
                    default_size_hint: 1, None
                    size_hint_y: None
                    height: self.minimum_height
                    orientation: 'vertical'
                    spacing: sp(2)

            FileChooserListView:
                id: filechooser
                size_hint_x: 0.7
                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)

''')


class LstPOPDriveBtn(BoxLayout):
    filechooser = ObjectProperty(None)

    def on_select_button(self, button):
        sm.current_screen.popup.start_path = button.text
        sm.current_screen.popup.title = '読み込み中 ' +  button.text


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

        if platform == 'win':
            import win32api
            
            dv = win32api.GetLogicalDriveStrings()
            
            self.drive_list.data = []
            btn_list = dv.split('\000')[:-1]   #ドライブ名をリストに、最後につく余分な空白は削除
            for btn_list_any in btn_list:
                self.drive_list.data.append({'value': btn_list_any})
        
        else:
            self.drive_list.width = 0
            self.drive_list.size_hint_x = 0

    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 Test(Screen):

    begin_path_dsp = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(Test, self).__init__(**kwargs)

        self.popup = LstPOP(load=self.testSelectLoad \
                          , cancel=self.testSelectCancel \
                          , start_path='E:\\')
        self.popup.open()

    def testSelectLoad (self, path, filename):
        print('読み込みが押されたがや')
        self.popup.dismiss()
        sys.exit()

    def testSelectCancel(self):
        print('キャンセルが押されたがや')
        self.popup.dismiss()
        sys.exit()


class TestApp(App):
    def build(self):

        sm.add_widget(Test(name='test'))

        return sm

if __name__ == '__main__':
    TestApp().run()

今回、Windows環境の時と、そうでないときは、表示が変わるようにしました。

まずWindows環境の時。起動時は初期値がE:\なので、E:\の内容が出る。

この段階で、workを押下で、E:\workの内容が出る。

そして、左側のQ:\を押下で、Q:\の内容が出る。

ここのworkを押下すると、Q:\workの内容が出る。

全く同じソースをUbuntuで動かす。左側のドライブ名は表示されない。

初期値がWindows用のE:\なので、エラーになるが適当なパスを拾ってくるようだ。

ここの../を押下することによって、現在地がわかる。

filelistのfontsにいたようである。

もちろん、テストデータ用のパスも表示できる。

 

 

 

プラットフォーム毎で処理を変える方法

 



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

        if platform == 'win':
            import win32api
            
            dv = win32api.GetLogicalDriveStrings()
            
            self.drive_list.data = []
            btn_list = dv.split('\000')[:-1]   #ドライブ名をリストに、最後につく余分な空白は削除
            for btn_list_any in btn_list:
                self.drive_list.data.append({'value': btn_list_any})
        
        else:
            self.drive_list.width = 0
            self.drive_list.size_hint_x = 0

Windowsのとき

win32apiのなかに、GetLogicalDriveStrings()というのがあるのですが、これは論理ドライブを文字列で表示させるというもの。

私のWindows機のドライブ構成が、C:\, E:\ Q:\となっているので、これを実行させると、

と、各ドライブの後ろにNULLが付いた形で出力される。

これをsplitを使って、分離とリスト化を同時に行う。便利だ。

(なおこれは、Python kivy: how to use filechooser access files outside C drive https://stackoverflow.com/questions/26028235/python-kivy-how-to-use-filechooser-access-files-outside-c-drive のAnswerをかなり参考にしました。)

Linuxなど、Windows以外のとき

そして、Windows以外は、ドライブ表示部分を非表示にするわけですが、幅関係を0にすることで実現した。

(なおこれは、Hiding and showing a widget in Kivy https://stackoverflow.com/questions/41985573/hiding-and-showing-a-widget-in-kivy を参考にしました。)

なお、kivyのwidgetにはHiddenの指定がないようです。

RecyecleViewのボタンの情報を画面に反映させる方法

ドライブが表示されたので、当然左側のツリーもそれに合わせて変更させなければなればいけませんが、その処理。

kvファイルの部分は、RecyeleViewのviewclassに、Button付きのLstPOPDriveBtnを指定し、そのLstPOPDriveBtnでは、押下するとon_select_button(self)が起動される。


<LstPOPDriveBtn>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            size: self.size
            pos: self.pos
    value: ''
    Button:
        text: root.value
        background_normal: ''
        background_color: 0.5, 0.5, 0.75, 1
        color: 1, 1 ,1 ,1
        on_press: root.on_select_button(self)

<LstPOP>:
    id:lst_pop
    size_hint: 0.9, 0.9
    drive_list: drive_list
    auto_dismiss: False
        
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: 'vertical'
        BoxLayout:
            orientation: 'horizontal'

            RecycleView:
                id: drive_list
                size_hint_x: 0.3
                scroll_type: ['bars', 'content']
                scroll_wheel_distance: sp(30) #スクロール速度
                bar_width: sp(10)
                viewclass: 'LstPOPDriveBtn'
                RecycleBoxLayout:
                    default_size: None, sp(40)
                    default_size_hint: 1, None
                    size_hint_y: None
                    height: self.minimum_height
                    orientation: 'vertical'
                    spacing: sp(2)

 

で、そのon_select_button(self)では、screenmanagerのcurrent_screenの更にpopupの中身を変更する。

start_pathに値をセットするだけで、ツリーの中身が更新される。


class LstPOPDriveBtn(BoxLayout):
    filechooser = ObjectProperty(None)

    def on_select_button(self, button):
        sm.current_screen.popup.start_path = button.text
        sm.current_screen.popup.title = '読み込み中 ' +  button.text

上のソースは、呼び出している方のインスタンス変数を操作することによって実現される。


class Test(Screen):

    begin_path_dsp = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(Test, self).__init__(**kwargs)

        self.popup = LstPOP(load=self.testSelectLoad \
                          , cancel=self.testSelectCancel \
                          , start_path='E:\\')
        self.popup.open()


そして、このkv18_3c.pyをfilelist.pyに反映させるとこうなる。

filelist.py


import os
import sys
import json

from kivy.app import App
from kivy.lang import Builder
from kivy.utils import platform

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 PathRead:

    def __init__(self, **kwargs):

        self.path_json = None
        self.notfound = False
        self.err = False

        try:
            #raise BaseException('やっちまったな')
            with open(path_inf, 'r') as fd:

                self.path_json = json.load(fd)

        except FileNotFoundError:

            self.notfound = True

        except PermissionError as err:
            self.msg = '保存データを読み込もうとしましたが、読み込み許可がありません。\n'
            self.msg += '読みこもうとしたパス・ファイルは、' + path_inf + 'です。\n'
            self.msg += '保存データの読み込み許可を確認するか、パス・ファイルが適当かを確認してください。\n\n'
            self.msg += str(err)

            content = MsgboxOkWithPost ( \
                btn_ok=self.btn_ok, \
                btn_ok_post=kwargs['btn_ok_post'], \
                message_text=self.msg)
            self.popup = Popup(title='読み込み許可エラー', content=content)
            self.popup.open()

            self.err = True

        except IOError as err:
            self.msg = '保存データを読み込もうとしましたが、読み込めません。\n'
            self.msg += '読み込み先機器に問題があるようです。\n'
            self.msg += '読み込み先機器の状態を確認してください。\n\n'
            self.msg += str(err)

            content = MsgboxOkWithPost ( \
                btn_ok=self.btn_ok, \
                btn_ok_post=kwargs['btn_ok_post'], \
                message_text=self.msg)
            self.popup = Popup(title='I/Oエラー', content=content)
            self.popup.open()

            self.err = True


        except BaseException as err:
            self.msg = '保存データを読み込もうとしましたが、何らかのエラーが起きました。\n\n'
            self.msg += str(err)

            content = MsgboxOkWithPost ( \
                btn_ok=self.btn_ok, \
                btn_ok_post=kwargs['btn_ok_post'], \
                message_text=self.msg)
            self.popup = Popup(title='その他のエラー', content=content)
            self.popup.open()

            self.err = True

    def btn_ok(self):
        self.popup.dismiss()


class PathWrite:
    def __init__(self, **kwargs):

        self.err = True

        path_inf_dict = kwargs['path_inf_dict']
        self.pd = {}
        self.pd['begin_path'] = path_inf_dict['begin_path']

        try:
            #raise BaseException('やっちまったな')
            with open(path_inf, 'w') as fd:
                json.dump(self.pd, fd)
                self.err = False

        except PermissionError as err:
            self.msg = '保存データを書き込もうとしましたが、書き込み許可がありません。\n'
            self.msg += 'パスは、' + self.pd['begin_path'] + 'です。\n'
            self.msg += '保存先の書き込み許可を確認するか、パスが適当かを確認してください。\n\n'
            self.msg += str(err)

            content = MsgboxOkWithPost ( \
                btn_ok=self.btn_ok, \
                btn_ok_post=kwargs['btn_ok_post'], \
                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 = MsgboxOkWithPost ( \
                btn_ok=self.btn_ok, \
                btn_ok_post=kwargs['btn_ok_post'], \
                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 = MsgboxOkWithPost ( \
                btn_ok=self.btn_ok, \
                btn_ok_post=kwargs['btn_ok_post'], \
                message_text=self.msg)
            self.popup = Popup(title='その他のエラー', content=content)
            self.popup.open()

    def btn_ok(self):
        self.popup.dismiss()


class MsgboxOk(BoxLayout):
    message_text = StringProperty()
    btn_ok = ObjectProperty(None)


class MsgboxOkWithPost(BoxLayout):
    message_text = StringProperty()
    btn_ok = ObjectProperty(None)
    btn_ok_post = ObjectProperty(None)


class SetSCN(Screen):
    begin_path_dsp = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(SetSCN, self).__init__(**kwargs)

    def setBeginPathCheck(self):
        if os.path.isdir(self.begin_path_dsp.text) == False:

            return False

        return True

    def setSelect(self):
        begin_path_result = self.setBeginPathCheck()

        if begin_path_result == True:
            self.popup = LstPOP(load=self.setSelectLoad, \
                                 cancel=self.setSelectCancel, \
                                 start_path=self.begin_path_dsp.text)
            self.popup.open()

        else:

            self.msg = '入力した開始パスが存在しません\n\n'
            self.msg += self.begin_path_dsp.text

            content = MsgboxOk (\
                 btn_ok=self.msgTestOk, \
                 message_text=self.msg)
            self.popup = Popup(title='入力エラー', content=content)
            self.popup.open()

            self.begin_path_dsp.text = sm.begin_path_previous

    def setSelectLoad (self, path, filename):
        self.begin_path_dsp.text = path
        sm.begin_path_previous = path
        self.popup.dismiss()

    def setSelectCancel(self):
        self.popup.dismiss()

    def msgTestOk(self):
        self.popup.dismiss()

    def msgTestCancel(self):
        self.popup.dismiss()

    def selfOk(self):

        begin_path_result = self.setBeginPathCheck()

        if begin_path_result == True:

            self.pd = {}
            self.pd['begin_path'] = self.begin_path_dsp.text

            pw = PathWrite( \
                path_inf_dict=self.pd, \
                btn_ok_post=self.pathwhite_ng)
            if pw.err == False:
                self.manager.get_screen('all').file_name.text = self.begin_path_dsp.text
                sm.current = 'all'

        else:

            self.msg = '入力した開始パスが存在しません\n\n'
            self.msg += self.begin_path_dsp.text

            content = MsgboxOk (\
                 btn_ok=self.msgTestOk, \
                 message_text=self.msg)
            self.popup = Popup(title='入力エラー', content=content)
            self.popup.open()

            self.begin_path_dsp.text = sm.begin_path_previous

    def selfCancel(self):
        sm.current = 'all'

    def pathwhite_ng(self):
        #
        # PathWhite()で異常だった時の処理
        #
        sys.exit()


class LstPOPDriveBtn(BoxLayout):
    filechooser = ObjectProperty(None)

    def on_select_button(self, button):
        sm.current_screen.popup.start_path = button.text
        sm.current_screen.popup.title = '読み込み中 ' +  button.text


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

        if platform == 'win':
            import win32api

            dv = win32api.GetLogicalDriveStrings()

            self.drive_list.data = []
            btn_list = dv.split('\000')[:-1]   #ドライブ名をリストに、最後につく余分な空白は削除
            for btn_list_any in btn_list:
                self.drive_list.data.append({'value': btn_list_any})

        else:
            self.drive_list.width = 0
            self.drive_list.size_hint_x = 0

    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):
        self.popup.dismiss()
        sys.exit()

    def msgFileNameInputErrorOk(self):
        self.popup.dismiss()

    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 (\
                btn_ok=self.msgOsErrorOk, \
                message_text=msg)
            self.popup = Popup(title='OSサポートエラー', content=content)
            self.popup.open()
            return

        #
        # AllSCN用の初期設定 ファイルパス読み込み
        #

        pr = PathRead(btn_ok_post=self.pathread_ng)

        if pr.path_json != None:
            #
            # PathRead()で正常だった時の処理
            #

            path_inf_begin = pr.path_json['begin_path']
            self.manager.get_screen('set').begin_path_dsp.text = path_inf_begin
            self.file_name.text = path_inf_begin
            sm.file_name_previous = path_inf_begin
            sm.begin_path_previous = path_inf_begin

        else:
            if pr.notfound == True:
                #
                # PathRead()で見つからなからった時の処理
                #

                self.manager.get_screen('set').begin_path_dsp.text = self.init_path
                self.file_name.text = self.init_path
                sm.file_name_previous = self.init_path
                sm.begin_path_previous = self.init_path

        if pr.err == False:

            # AllSCNのtree処理
            self.tv.add_node(TreeViewLabel(text='シンカリオン E1 とき'))
            self.tv.add_node(TreeViewLabel(text='シンカリオン 0 ひかり'))
            self.tv.add_node(TreeViewLabel(text='シンカリオン キハ32 鉄道ホビートレイン'))


    def pathread_ng(self):
        #
        # PathRead()で異常だった時の処理
        #
        sys.exit()


    def setupButtonClicked(self):
        #
        # SetSCNへの画面遷移時の内容
        #

        sm.current = 'set'

    def allSelect(self):
        self.popup = LstPOP(load=self.allSelectLoad, \
                             cancel=self.allSelectCancel, \
                             start_path=self.file_name.text)
        self.popup.open()

    def allSelectLoad(self, path, filename):
        self.file_name.text = path
        sm.file_name_previous = path
        self.popup.dismiss()

    def allSelectCancel(self):
        self.popup.dismiss()

    def allFileNameCheck(self):
        if os.path.isdir(self.file_name.text) == False:

            return False

        return True

    def allExec(self):
        file_name_result = self.allFileNameCheck()

        if file_name_result == True:
            sm.file_name_previous = self.file_name.text

        else:
            self.msg = '入力した開始パスが存在しません\n\n'
            self.msg += self.file_name.text

            content = MsgboxOk (\
                 btn_ok=self.msgFileNameInputErrorOk, \
                 message_text=self.msg)
            self.popup = Popup(title='入力エラー', content=content)
            self.popup.open()

            self.file_name.text = sm.file_name_previous

    def msgTestOk(self):
        self.popup.dismiss()

    def msgTestCancel(self):
        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.allSelect()
                Button:
                    size_hint_x: 0.1
                    text: '実行'
                    on_release: root.allExec()

        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: '了解'
            on_release:
                root.btn_ok()


<MsgboxOkWithPost>
    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.btn_ok()
                root.btn_ok_post()

<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()

<LstPOPDriveBtn>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            size: self.size
            pos: self.pos
    value: ''
    Button:
        text: root.value
        background_normal: ''
        background_color: 0.5, 0.5, 0.75, 1
        color: 1, 1 ,1 ,1
        on_press: root.on_select_button(self)

<LstPOP>:
    id:lst_pop
    size_hint: 0.9, 0.9
    drive_list: drive_list
    auto_dismiss: False
        
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: 'vertical'
        BoxLayout:
            orientation: 'horizontal'

            RecycleView:
                id: drive_list
                size_hint_x: 0.3
                scroll_type: ['bars', 'content']
                scroll_wheel_distance: sp(30) #スクロール速度
                bar_width: sp(10)
                viewclass: 'LstPOPDriveBtn'
                RecycleBoxLayout:
                    default_size: None, sp(40)
                    default_size_hint: 1, None
                    size_hint_y: None
                    height: self.minimum_height
                    orientation: 'vertical'
                    spacing: sp(2)

            FileChooserListView:
                id: filechooser
                size_hint_x: 0.7
                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: