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

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

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

Kivyは、Pythonを用いたNUI開発のためのオープンソースフレームワーク。マルチタッチなど多くの入力に対応したNUIアプリなどを開発することができます。多くの環境で動作するクロスプラットフォームです。

button

HTMLで用いる<button>タグです。

Q&A

解決済

1回答

2048閲覧

python + kivyのGridLayoutで、動的にボタンを配置したい

hiro04kon

総合スコア46

Kivy

Kivyは、Pythonを用いたNUI開発のためのオープンソースフレームワーク。マルチタッチなど多くの入力に対応したNUIアプリなどを開発することができます。多くの環境で動作するクロスプラットフォームです。

button

HTMLで用いる<button>タグです。

0グッド

0クリップ

投稿2021/06/29 12:15

編集2021/07/02 00:17

python3,kivyで一から勉強しております。
以前、pythonのみで同じことをしていましたが、スマホに対応させるために、kivyで書き直しています。

###やりたいこと
GridLayoutを用いて指定した数だけボタンを配置し、クリックすると属性をdisableにしたいのです。
しかし、以下のコードを書いてみましたが、ボタンが表示されず、真黒な画面にしかなりません。
layoutを別に定義しないといけないのでしょうか?教えてください。

kivy

1from kivy.app import App 2from kivy.uix.gridlayout import GridLayout 3from kivy.uix.floatlayout import FloatLayout 4from kivy.uix.button import Button 5 6tate = 4 7yoko = 5 8sosu = tate*yoko 9 10class kvfile(App): 11 def build(self): 12 return ZasekiWindow() 13 14class ZasekiWindow(GridLayout): 15 def __init__(self, **kwargs): 16 super(ZasekiWindow, self).__init__(**kwargs) 17 self.title = 'ボタン一覧' 18 self.buttons() 19 20 def buttons(self): 21 layout = GridLayout(cols = yoko) 22 id = 0 23 for i in range(tate): 24 for j in range(yoko): 25 id += 1 26 layout.add_widget(Button(text=str(id),on_press = lambda x: self.buttonClicked(str(id))) 27 ) 28 29 def buttonClicked(self,id): 30 print("クリックした") 31 instance.disable = False 32 33if __name__ == "__main__": 34 kvfile().run()

今後としては、データを読み取り、入力済みのボタンは、最初からdisableにしたいと考えています。
また、違うデータを読み取ったら再描画したいと考えています。

書き方がpythonの時と違ってしまい、戸惑っています。
よろしくお願いします。

###訂正後
最初は省略

python

1class kvfile(App): 2 title = 'チェック表' 3 4 def build(self): 5 layout = GridLayout(cols = yoko) 6 id = 0 7 for i in range(tate): 8 for j in range(yoko): 9 id += 1 10 layout.add_widget(Button(text=str(id),on_press = lambda n = id : self.buttonClicked(n)) 11 ) 12 return layout 13 14 def buttonClicked(self,instance): 15 print(instance,"クリックした") 16 instance.enabled = False 17 18if __name__ == "__main__": 19 kvfile().run()

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

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

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

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

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

guest

回答1

0

ベストアンサー

  • return ZasekiWindow()layout = GridLayout(cols = yoko)の二箇所でGridLayoutを作っているのは意図したものですか?
  • layout = GridLayout(cols = yoko)で作ったlayoutはどこにも繋がれていないので表示されないです。親にしたいwidget.add_widget(layout)として繋いであげてください。
  • titleを持っているのはAppclassです。
  • lambdaの落とし穴に引っかかっているのでこれを読んでおくのを薦めます。
  • Buttonを無効にしたい時は~~Buttonのinstance.enabled = False~~Buttonのinstance.disabled = Trueです。
  • on_press = lambda x: self.buttonClicked(str(id))xにButtonのinstanceが入ってます。

追記:3番目のcommentへの返事

instance.disabled = True にしたら、できました!書いていることは同じようでも、違うんですね…。

英語の意味的に同じであれば何でも良いってわけじゃないのでdisabled = Trueenabled = Falseに自由に置き換えたりはできないですね。因みにわからないことをどうやって調べてますか?今回のようにButtonを無効化したいのならkivy button disableでググれば答えがすぐに出てきますよ。

また、引数を instance, n = id にしてみましたが ... 動作しませんでした。

もしself.buttonClicked(n)の部分がそのままであればそうなりますね。buttonClicked()の定義を見ると第二位置引数はinstanceとなっているのでそこにはButtonのinstanceを渡さないといけないわけです。にも関わらず実際に渡しているのはself.buttonClicked(n)で分かる通りnという整数なので。

layoutoのボタンと、同時にappendで作ったボタンリストでは、違ったものになってしまい、困っています…

具体的にどう違うんですか?layout.childrenappendで作ったボタンリストの中身の順番が逆だということならそれで正常です。

追記:別のやり方

現在のcodeではlambdaのdefault引数を利用してidを結びつけてますが、idをButtonの属性として書き込む手もあるので状況に応じて使い分けると良いと思います。

def build(self): layout = GridLayout(cols = yoko) id = 0 for i in range(tate): for j in range(yoko): id += 1 button = Button(text=str(id), on_press=self.buttonClicked) button.my_id = str(id) layout.add_widget(button) return layout def buttonClicked(self, instance): print(instance.my_id,"クリックした") instance.enabled = False

投稿2021/07/01 12:06

編集2021/07/02 12:51
gottadiveintopy

総合スコア736

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

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

hiro04kon

2021/07/02 00:38

早速のご回答、ありがとうございます。 上記のアドバイスをもとに、訂正後を付け加えました。 lambdaの落とし穴…。その通りでした。勉強不足でした(全てにおいてですが…) 更に詳しくよろしいでしょうか? Buttonを無効にするため、instance.enabled = False としましたが、 ボタンが無効になりません。 多分、layoutを更新しないといけないと思うのですが、そのためには、Buttonをリスト化し、 クリックしたら、リストに変更を加え、それをもとに更新する、という流れであっているでしょうか? すいませんが、アドバイスよろしくお願いします。
gottadiveintopy

2021/07/02 01:07 編集

ごめんなさい、無効にするときは instance.disabled = True だったかもれないです。buttonのinstanceにさえ触れればlayoutは触らなくても大丈夫です。後titleをclass-levelで書きかえるのは古いやり方で今は使えないのでinstance経由で書き込むようにしてください。例えばbuild()内で self.title = ... 。後lambdaのdefault引数でn=idとして整数を渡してますが、lambdaの第一引数には常にbuttonのinstanceが渡されるのでこのdefault引数が使われる事は無いですね。多分やりたい事は lambda instance, n = id: ではないですか?
hiro04kon

2021/07/02 03:43

すぐにご回答、ありがとうございます。 instance.disabled = True にしたら、できました!書いていることは同じようでも、違うんですね…。 lambdaの引数については、どのボタンを押したかを調べるために、idの番号をそのまま利用しようとしたのですが、instanceの中身を見てみると、<kivy.uix.button.Button object at 0x000002461D08EC80>のようになっていました。varsで中を見てみましたが、idはどこへやら… また、引数を instance, n = id にしてみましたが、 AttributeError: 'int' object has no attribute 'disabled' となり、動作しませんでした。 本当は、オブジェクトの名前を、button1のように分かりやすいものにできたら、今後の作業に扱いやすいかな、と思っていただけです…。 定義buttonClicked から、ボタンのidを取得できれば、形は問わないのですが、layoutoのボタンと、同時にappendで作ったボタンリストでは、違ったものになってしまい、困っています…
hiro04kon

2021/07/02 11:18 編集

またまたご回答、ありがとうございます。 分からないものは、とりあえずググって調べています。1つのボタンを無効にする書き方はありますが、それを「どのボタンでも」動作するようにするには、どう書いたらいいのか、いつも分かりません…。 self.buttonClicked()は、押されたボタンを認識し、その番号?をもとに、csv等に記録、すでに押されてあれば、次見たときには既にdisabledになっているようにしたいのです。引数は、調べたときに第2引数にinstanceとあったので、それを使っていました… layout.add_widget(Button(…))を、 buttons.append(Button(…)) ※…は同じ でButtonをリストに記録したら、オブジェクトのメモリアドレスの値が違っていた、という意味です。 self.buttonClicked()の引数を、「instance,id」に、def の引数も「self,instance,id」にしたところ、 ボタンのidも取得できました。これで何とかなりそうです! 何度もお答えいただき、ありがとうございます!
gottadiveintopy

2021/07/02 13:21 編集

> ボタンのidも取得できました。これで何とかなりそうです! ????
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問