質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

5163閲覧

(kivy)Recycleviewを使用した表におけるToggleButtonの挙動について

roshi10011

総合スコア8

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2017/11/15 08:38

編集2017/11/15 08:40

###やりたいこと
kivyを使用したGUIにおいて、スクロールできる表を作成しています。
表のうち、いずれかのセルをクリックするとそのセルを含む行が選択された状態になり、再度クリックすることで画面遷移をしたいです。

###現状
Recycleviewを使用して表を作成し、表のセルをToggleButtonで表現しました。
ToggleButtonが押されたら、その行に含まれるToggleButtonの状態を変化されるように作っております。

###困っていること
Recycleviewで作成した表にToggleButtonを埋め込み、ボタンを押下したところ、意図しないボタンまで押下された状態になりました。
更に表を何回かスクロールしたところ、押下したボタンの状態が勝手に変化していることを確認しました。

これはkivy側の仕様なのでしょうか?

python

1from random import sample 2from string import ascii_lowercase 3 4from kivy.app import App 5from kivy.lang import Builder 6from kivy.uix.boxlayout import BoxLayout 7 8 9kv = """ 10<Row@BoxLayout>: 11 canvas.before: 12 Color: 13 rgba: 0.5, 0.5, 0.5, 1 14 Rectangle: 15 size: self.size 16 pos: self.pos 17 value: '' 18 ToggleButton: 19 text: root.value 20 21<Test>: 22 canvas: 23 Color: 24 rgba: 0.3, 0.3, 0.3, 1 25 Rectangle: 26 size: self.size 27 pos: self.pos 28 rv: rv 29 orientation: 'vertical' 30 GridLayout: 31 cols: 3 32 rows: 2 33 size_hint_y: None 34 height: dp(108) 35 padding: dp(8) 36 spacing: dp(16) 37 Button: 38 text: 'Populate list' 39 on_press: root.populate() 40 Button: 41 text: 'Sort list' 42 on_press: root.sort() 43 Button: 44 text: 'Clear list' 45 on_press: root.clear() 46 BoxLayout: 47 spacing: dp(8) 48 Button: 49 text: 'Insert new item' 50 on_press: root.insert(new_item_input.text) 51 TextInput: 52 id: new_item_input 53 size_hint_x: 0.6 54 hint_text: 'value' 55 padding: dp(10), dp(10), 0, 0 56 BoxLayout: 57 spacing: dp(8) 58 Button: 59 text: 'Update first item' 60 on_press: root.update(update_item_input.text) 61 TextInput: 62 id: update_item_input 63 size_hint_x: 0.6 64 hint_text: 'new value' 65 padding: dp(10), dp(10), 0, 0 66 Button: 67 text: 'Remove first item' 68 on_press: root.remove() 69 70 RecycleView: 71 id: rv 72 scroll_type: ['bars', 'content'] 73 scroll_wheel_distance: dp(114) 74 bar_width: dp(10) 75 viewclass: 'Row' 76 RecycleBoxLayout: 77 default_size: None, dp(56) 78 default_size_hint: 1, None 79 size_hint_y: None 80 height: self.minimum_height 81 orientation: 'vertical' 82 spacing: dp(2) 83""" 84 85Builder.load_string(kv) 86 87 88class Test(BoxLayout): 89 90 def populate(self): 91 self.rv.data = [{'value': ''.join(sample(ascii_lowercase, 6))} 92 for x in range(50)] 93 94 def sort(self): 95 self.rv.data = sorted(self.rv.data, key=lambda x: x['value']) 96 97 def clear(self): 98 self.rv.data = [] 99 100 def insert(self, value): 101 self.rv.data.insert(0, {'value': value or 'default value'}) 102 103 def update(self, value): 104 if self.rv.data: 105 self.rv.data[0]['value'] = value or 'default new value' 106 self.rv.refresh_from_data() 107 108 def remove(self): 109 if self.rv.data: 110 self.rv.data.pop(0) 111 112 113class TestApp(App): 114 def build(self): 115 return Test() 116 117 118if __name__ == '__main__': 119 TestApp().run()

kivyのサンプルプログラムより抜粋・一部変更あり

###備考
python: 3.4.2
kivy: 1.10.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

自分なりに調べた結果、いくつか面白い事が分かりました。
検証コード

以下は上の検証コードを実行して、
valueにAを入れて「Insert new item」を押す。
valueにABを入れて「Insert new item」を押す。
同じ要領で ABC, ABCD, ABCDE と追加していった時の標準出力です。

text

1----insert---- 2Rowの順番 [] 3add_widget: 377 () {} 4----insert---- 5Rowの順番 [('377', 'A')] 6remove_widget: 377 () {} 7add_widget: 377 () {} 8add_widget: 395 () {} 9----insert---- 10Rowの順番 [('395', 'A'), ('377', 'AB')] # A 11remove_widget: 395 () {} 12remove_widget: 377 () {} 13add_widget: 395 () {} 14add_widget: 377 () {} 15add_widget: 413 () {} 16----insert---- 17Rowの順番 [('413', 'A'), ('377', 'AB'), ('395', 'ABC')] # B 18remove_widget: 413 () {} 19remove_widget: 377 () {} 20remove_widget: 395 () {} 21add_widget: 413 () {} 22add_widget: 377 () {} 23add_widget: 395 () {} 24add_widget: 431 () {} 25----insert---- 26Rowの順番 [('431', 'A'), ('395', 'AB'), ('377', 'ABC'), ('413', 'ABCD')] # C 27remove_widget: 431 () {} 28remove_widget: 395 () {} 29remove_widget: 377 () {} 30remove_widget: 413 () {} 31add_widget: 431 () {} 32add_widget: 395 () {} 33add_widget: 377 () {} 34add_widget: 413 () {} 35add_widget: 449 () {}

出力中にある数字はWidget(Row)のuid(固有のID)です。
一つ分かったのは新しいRowが追加されるたびに、それまでにあったRowの順番が反転してることです。例えば#Aでは 395,377 の順なのが #Bでは新しく追加された413の後に 377,395 の順に(つまり逆順に)並んでいます。#Bから#Cにかけても同じです。
二つ目は各Rowに割り当てられているvalueが固定されてないことで、#Bでは377に'AB'が割り当てられているのに#Cでは'ABC'が割り当てられています。

内部でのRowの順番の変化とRowに割り当てるvalueの変化がToggleButtonのOn-Offが切り替わった様に見える原因だと思います。

更に表を何回かスクロールしたところ、押下したボタンの状態が勝手に変化していることを確認しました。

これも出力をみるとスクロール中に激しくadd_widget()とremove_widget()が呼び出されてました。
ここからは個人的な予想で検証はしていないんですが、RecycleViewは巨大な量のデータを扱えることをうりにしているので、その為の処理負荷対策なんじゃないかと思います。例えばスクロールによって'X'というvalueを表示する必要が無くなり、代わりに'Y'というvalueを表示する必要が生じた時、RecycleViewは’X'の表示に使っていたRowを’Y'の表示に使い回してRowのインスタンスの数を節約しているんじゃないかと。

スクロールの部分はともかくinsertによってvalueの割り当て直しが起きているのは事実なので、valueとそれに対応するRowは固定されない前提で扱わないといけないと言えると思います。

投稿2017/11/15 12:37

編集2017/11/18 03:39
gottadiveintopy

総合スコア736

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

roshi10011

2017/11/16 00:41

詳細にコメントしていただきありがとうございます! 納得と理解ができましたので、ベストアンサーとさせていただきます。
guest

0

確かにそんな感じだったと記憶しています。
on_press
om_release
どちらにしても触ったところで判定を返すようになってるはずです。
まだ試してませんが、on_touch_系を使うとより厳格な指示にできるかもしれません。

投稿2017/11/15 09:11

Equestria

総合スコア27

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問