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

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

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

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

Python

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

Q&A

解決済

2回答

1999閲覧

kivymd でScreen Class内に条件でダイナミックにボタンを追加・削除しようとしても問題が起きる。.add_widget ().remove_widget()の使い方がわからない。

JK_Taro

総合スコア20

Kivy

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

Python

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

0グッド

0クリップ

投稿2021/09/01 07:18

KivyMD + kivy + python を使って次のプログラムのsecond_screenに遷った際にのとくていIDのMDBoxLayout内に条件に応じてボタンを追加したり削除したいのですが、うまくいきません。
簡単なプログラムの流れは次です。
1.main_screen から go_second_screen ボタンを押す
2.second_screenに移行する際、on_enter()が実行されてsystem_statusでボタンを追加するかが決められる
3.必要であればsecond_screenの特定IDのMDBoxLayoutにボタンが追加表示される

まず最初に単純に .add_widgetだけをadd_button()メソッドで使います。そうすると、一応ボタンは表示されますが width=と pos=のパラメータが無視され、ボタンが画面最下位に左に最小サイズで表示され意図したデザインで表示されません。下に余分なコードを抜いて単純化したコードをこの話をベース書きました。

2番めの問題としては、add_widgetだけですと、second_screenにアクセスするたびに条件が合えば同じボタンが増えていってしまうので、画面がmain_screenに移るときに追加したボタンを削除します。 remove_widgetを使います。widget関数をクラス内関数に場所をかえて、widgetはすべてself.widgetに書き換えます。ここではテストとしてon_cancel()に削除機能を書きました。すると次のエラーでプログラム起動時に即クラッシュします。
何が悪いのかわからず、行き詰まっています。よろしくお願いします。

remove_widgetを加えたときにでるエラーが下です

remove_widget widget.funbind('pos_hint', self._trigger_layout) AttributeError: 'NoneType' object has no attribute 'funbind'

該当のソースコード

Python

1############ MainScreen class ################ 2 3# go_second_screen button exist 4 5########### SecondScreen class ############### 6 7class SecondScreen(Screen): 8 def __init__(self, **kwargs): 9 super(SecondScreen, self).__init__(**kwargs) 10 Clock.schedule_once(self.on_start,1) 11 12 # this is for 2nd problem 13 #widget = None 14 15 def on_start(self, *args): 16 #do some stuff 17 18 def on_enter(self): 19 Clock.schedule_once(self.system_status_check) 20 self.system_status() 21 22 def system_status(self): 23 #get condition from other class 24 if SystemControl.system_flag() == True: 25 self.add_button() 26 27 def add_button(self): 28 #this kind of works but not intended way (2nd issue rewrited everything with self.widget) 29 widget = MDRaisedButton( 30 text="Extra function button", 31 md_bg_color=(0.3, 1, 1, 0.5), 32 font_size = "20sp", 33 width = self.ids.dialog.width, 34 pos = (self.ids.dialog.x, self.ids.dialog.top + self.height) 35 ) 36 self.ids.add_button_box.add_widget(widget) 37 38 def on_cancel(self): 39 #これが2番めの問題 40 #widgetをクラスの変数にして 全部のwidgetをself.widgetに書き換えた前提です 41 self.ids.add_button_box.remove_widget(self.widget) 42 43 def on_save(self): 44 #ここでなんかする 45 46################ second_screen.kv##################### 47 48<SecondScreen>: 49 id:second_screen 50 name:'second_screen' 51. 52#some sliders and informations 53. 54 #New button should be added just below in the box layout 55 MDBoxLayout: 56 id: add_button_box 57 orientation: "vertical" 58 59 MDBoxLayout: 60 id:dialog 61 orientation: "horizontal" 62 63 MDRaisedButton: 64 id:save_button 65 text:"Save and Exit" 66 width:dialog.width/2 67 md_bg_color: 1, 0, 1, 1 68 on_release: 69 root.on_save() 70 app.root.current = 'main_screen' 71 72 MDRaisedButton: 73 text:"Cancel" 74 width:dialog.width/2 75 on_release: 76 root.on_cancel() 77 app.root.current = 'main_screen' 78 79######### MainSM class ############## 80 81Builder.load_file("./kv/main_screen.kv") 82Builder.load_file("./kv/second_screen.kv") 83. 84. 85#multiple screens 86 87class MainSM(ScreenManager): 88 pass 89 90############## main.kv ############### 91 92<MainSM>: 93 id: screen_manager 94 transition: NoTransition() 95 Main_Screen: 96 Second_Screen: 97 . 98 . 99 100########## MainApp class ############# 101 102class MainApp(MDApp): 103 104 def build(self): 105 106 return MainSM() 107 108MainApp().run() 109 110######################################

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

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

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

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

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

guest

回答2

0

自己解決

def init(self, **kwargs):
super(SecondScreen, self).init(**kwargs)
Clock.schedule_once(self.on_start,1)

↑のイニシャライズ部分を削除してon_enterのときに必要な処理をするように書き換えたら、どういうわけだか動くようになりました。
>size_hintやpos_hintのようなhintの付くpropertyを使ってlayoutする事を心がけ、bindingはどうして>も必要な時にだけ用いるようにしてください。
↑こちらも、このようにsize_hintで書きましたら、正常に表示されるようになりました。kivyMDですと.kvファイルのMDボタンなどにはsize_hintなどを使うと逆に問題を起こすので避けるべきとの先入観を持ってました。

投稿2021/09/19 09:18

JK_Taro

総合スコア20

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

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

0

エラーメッセージが示しているのはself.ids.add_button_box.remove_widget(self.widget)が実行された時の引数のself.widgetNoneだという事だと思うので、SecondScreen.add_button()が呼ばれる前にSecondScreen.on_cancel()が呼ばれている可能性を疑うと良いと思いますよ。

def add_button(self): #this kind of works but not intended way (2nd issue rewrited everything with self.widget) widget = MDRaisedButton( text="Extra function button", md_bg_color=(0.3, 1, 1, 0.5), font_size = "20sp", width = self.ids.dialog.width, pos = (self.ids.dialog.x, self.ids.dialog.top + self.height) )

なんですけど、このような形でwidthposを指定してもこの瞬間のdialogの位置や大きさでMDRaisedButtonの位置や大きさを初期化しているだけであって、その後dialogの位置や大きさが変わってもMDRaisedButton側の位置や大きさは更新されないことに注意してください。もしその後も更新させたいなら例えば

python

1 def add_button(self): 2 widget = MDRaisedButton(...) 3 self.ids.dialog.bind(width=widget.setter('width'))

といった風にbindingを張る必要があります。これに関しては記事を書いたので参考にしてみてください。ただそもそもなんですがwidgetのlayoutにbindingを用いる事は推奨されていないです。できるだけsize_hintpos_hintのようなhintの付くpropertyを使ってlayoutする事を心がけ、bindingはどうしても必要な時にだけ用いるようにしてください。

参考:Layoutことはじめ

投稿2021/09/04 11:46

gottadiveintopy

総合スコア736

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

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

JK_Taro

2021/09/19 09:18

勉強になりました、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問