KivyのFileChooserのListViewで複数選択したときも色を変えてどれを選択したか分かるようにするの巻

KivyのFileChooserのListViewで複数選択したときも色を変えてどれを選択したか分かるようにするの巻

はじめに

KivyのFileChooserで、ListViewを使って複数のファイルをLoadしようとしたときに、どのファイルを選択したかが分かりにくいのです。

どういうことかというと、普通にFileChooserを作ると下の動画のような状態です。

と、こんな感じでListViewでは複数のファイルを選択したときに、最後にクリックしたファイルしか背景色が変わりません。ついでに、背景色が変わったファイルのところをクリックしても背景色が元に戻りません。ですので、どのファイルを選択したかが分からなくなります、ワタシは。。。

IconViewであれば、選択したファイルの背景色がちゃんと変わっているのですけどね(動画の最後の部分です)。。。という状態です。

ファイル名が長い時はListViewのほうが見やすいので使いたいのですが、いちいちIconViewに戻ってどれが選択されているか確認するのもなんだかなぁなので、ListViewの時でもどのファイルを選択したかが分かるようにしたいぞう。

ということで、

という感じにしたときの方法の備忘録です。

もっと良い方法があるかもしれませんけど。

方法

方法としては(あ、Pythonです)

.pyファイルで、

  1. 背景を変えるためにColorとRectangleをimportしておく
  2. 以下の処理を行う関数を設定する(関数名はreset_colorとしています)
    1. treeviewの要素オブジェクトをforで取り出す
    2. ListViewの行の背景色を設定し、(奇数行oddと偶数行evenでストライプになるように)一旦、設定した元の背景色に戻す
    3. 要素オブジェクトがselectedの状態であれば、selected用の背景色に変える

.kvファイルで、

  1. FileChooser全体の背景色を設定する
  2. 作成した関数が、ListViewを選択するボタンを押して離したとき(on_release)と、
    ListViewでファイルを選択したとき(on_selection)に発動するようにする

としています。

*背景色はデフォルトの設定から変更した方がわかりやすいので変更しています(ここではLoadDialogだけ)

コード

コードはKivyのオフィシャルサイトのFileChooserの項目のコードを都合のいいようにモディファイしたものをベースにしています。https://kivy.org/doc/stable/api-kivy.uix.filechooser.html

コメントを付けているところが今回の件のために追記したところで、

上述の方法の項における数字に対応しています(つもり)。

なお、FileChooserのidをfcとしています。

main.py

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup

# .py 1. for changing background color
from kivy.graphics import Color, Rectangle


import os


class LoadDialog(FloatLayout):
    load = ObjectProperty(None)
    cancel = ObjectProperty(None)


class SaveDialog(FloatLayout):
    save = ObjectProperty(None)
    text_input = ObjectProperty(None)
    cancel = ObjectProperty(None)


class Root(FloatLayout):
    loadfile = ObjectProperty(None)
    savefile = ObjectProperty(None)
    text_input = ObjectProperty(None)

    def dismiss_popup(self):
        self._popup.dismiss()

    def show_load(self):
        content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
        self._popup = Popup(title="Load file", content=content,
                            size_hint=(0.9, 0.9))
        self._popup.open()

    def show_save(self):
        content = SaveDialog(save=self.save, cancel=self.dismiss_popup)
        self._popup = Popup(title="Save file", content=content,
                            size_hint=(0.9, 0.9))
        self._popup.open()

    def load(self, path, filename):
        with open(os.path.join(path, filename[0])) as stream:
            self.text_input = stream.read()

        self.dismiss_popup()

    def save(self, path, filename):
        with open(os.path.join(path, filename), 'w') as stream:
            stream.write(self.text_input)

        self.dismiss_popup()

    # .py 2. define function
    # pass fc.selection in the .kv file to the function as selectedfiles
    def reset_color(self, selectedfiles):

        #process when viewmode is listView 
        if self._popup.content.ids.fc.view_mode == 'list':
            for i in self._popup.content.ids.fc.children[0].children[0].children[0].ids.treeview.children:

                #Return the background color to the original color
                with i.canvas.before:
                    Color(rgba=(29/255,49/255,86/255,1) if i.odd else (39/255,57/255,90/255,1))

                    #change the color with parent width
                    Rectangle(size=(i.parent.size[0], i.size[1]) if i.parent else (0,0), pos=(i.parent.x, i.y) if i.parent else (1,1)) 

                #if selected rows are exist, change background color
                #(attribute 'selected' is only added after selected a file)
                if hasattr(i, 'selected') and i.selected:
                    with i.canvas.before:
                        Color(55/255, 65/255, 85/255, 1.0)

                        #change the color with parent width
                        Rectangle(size=(i.parent.size[0], i.size[1]) if i.parent else (0,0), pos=(i.parent.x, i.y) if i.parent else (1,1)) 

                else:
                    pass
        else:
            pass

class Editor(App):
    pass


Factory.register('Root', cls=Root)
Factory.register('LoadDialog', cls=LoadDialog)
Factory.register('SaveDialog', cls=SaveDialog)


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

editor.kv

#:kivy 2.0.0

Root:
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            size_hint_y: 1
            Button:
                text: 'Load'
                on_release: 
                    root.show_load()
            Button:
                text: 'Save'
                on_release: root.show_save()

<LoadDialog>:
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: "vertical"
        BoxLayout:
            size_hint_y: None
            height: sp(52)
            Button:
                text: 'Icon View'
                on_press: fc.view_mode = 'icon'
            Button:
                text: 'List View'
                on_press: fc.view_mode = 'list'
                # .kv 2. fire function on_release
                on_release: app.root.reset_color(fc.selection)

        FileChooser:
            id: fc
            filters:['*.text']
            # enable multiselection
            multiselect: True
            path: '.'
            # .kv 2. fire function on_selection
            on_selection: app.root.reset_color(fc.selection)
            FileChooserIconLayout
            FileChooserListLayout

        BoxLayout:
            size_hint_y: None
            height: 100
            Button:
                text: "Cancel"
                on_release: root.cancel()

            Button:
                text: "Load"
                on_release: root.load(filechooser.path, filechooser.selection)

<SaveDialog>:
    text_input: text_input
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: "vertical"
        FileChooserListView:
            id: filechooser
            on_selection: text_input.text = self.selection and self.selection[0] or ''

        TextInput:
            id: text_input
            size_hint_y: None
            height:100 
            multiline: False

        BoxLayout:
            size_hint_y: None
            height:100 
            Button:
                text: "Cancel"
                on_release: root.cancel()

            Button:
                text: "Save"
                on_release: root.save(filechooser.path, text_input.text)

# .kv 1. set background color of FileChooser
<FileChooser>:
    canvas.before:
        Color:
            rgba: 29/255,49/255,86/255,1 #Navy blue
        Rectangle:
            size: self.size

で、こうなる、と。

めでたしめでたし。

おわりに

KivyのFileChooserのListviewが少し分かりやすくなりました。

どこをどういじればいいか試行錯誤に難儀しましたので備忘録として残しておきます。

ついでにうっかりどなたかのお役に立てばうれしいです。

実施環境

Python 3.9.4

Kivy 2.0.0

macOS Catalina ver.10.15.7

参考にしたサイトなど

Kivyのオフィシャルサイト Filechooser; https://kivy.org/doc/stable/api-kivy.uix.filechooser.html

広告



https://business.xserver.ne.jp/

https://www.xdomain.ne.jp/

★LOLIPOP★

.tokyo

MuuMuu Domain!