Python kivy shows only last label which generated in loop - python

last day i came across on very irritate behave my program.
Mabye at first i show screenshot, describe error, and finnaly shows code.
As you can see, Coin is a button which contains a few sub-buttons. I generate these widgets in loop and dynamically add to layer.
But it works correctly only in last iterating.
Please look now at code.
Here is a code "Coin Button: class.
Note this, that for example: button apx, Refresh, H, DH and D is a member of one class
class SubButtonRefreshCoinData(Button):
def __init__(self, coin_name, **kwargs):
super(SubButtonRefreshCoinData, self).__init__(**kwargs)
self.CoinName = coin_name
self.text = "Refresh"
def on_press(self):
PopupNotImplementedItem().open()
class SubButtonCoinName(Button):
def __init__(self, **kwargs):
super(SubButtonCoinName, self).__init__(**kwargs)
self.text = r'[color=ff3333]{}[/color]'.format(kwargs["text"])
self.markup = True
self.font_size='20sp'
class SubButtonGoToCoinHistory(Button):
def __init__(self, coin_name, **kwargs):
super(SubButtonGoToCoinHistory, self).__init__(**kwargs)
self.CoinName = coin_name
self.text = "H"
def on_press(self):
subprocess.Popen(f'py HistoryWindow.py {self.CoinName}', shell=True)
class SubButtonDeleteCoin(Button):
def __init__(self, coin_name, **kwargs):
super(SubButtonDeleteCoin, self).__init__(**kwargs)
self.CoinName = coin_name
self.text = "D"
def on_press(self):
PopupNotImplementedItem().open()
class SubButtonDeleteCoinHistory(Button):
def __init__(self, coin_name, **kwargs):
super(SubButtonDeleteCoinHistory, self).__init__(**kwargs)
self.CoinName = coin_name
self.text = "DH"
print("sdfecvsdcdfwreafsq3456tedcqr4536wtger34r5cedwt4g5aced erf34")
def on_press(self):
PopupNotImplementedItem().open()
Now, please take a look on the Builder class these pieces:
class CoinButton(FloatLayout):
def __init__(self, coin_name, **kwargs):
super(CoinButton, self).__init__(**kwargs)
self.CoinName = coin_name
topHalfLayout = BoxLayout(pos_hint={"top":1}, size_hint = [1,0.49], orientation = "horizontal")
topHalfLayout.add_widget(SubButtonCoinName(text=str(self.CoinName)))
topHalfLayout.add_widget(SubButtonRefreshCoinData(self.CoinName))
self.add_widget(topHalfLayout)
downHalfLayout = BoxLayout(pos_hint={"down":1}, size_hint = [1,0.49], orientation = "horizontal")
downHalfLayout.add_widget(SubButtonGoToCoinHistory(self.CoinName))
downHalfLayout.add_widget(SubButtonDeleteCoinHistory(self.CoinName))
downHalfLayout.add_widget(SubButtonDeleteCoin(self.CoinName))
self.add_widget(downHalfLayout)
As you can see, everything seems be correct, but only ONE picece of class is visible.
In class SubButtonDeleteCoinHistory i tired primitive debug this problem to see did this code is already run. As i saw in console, text was printed 3 times, that is correct value.

I am not really sure what is happening here, but I take a shot. I believe that every time a CoinButton is initialized, the bottom button were added with respect to the FloatLayout which and using the pos_hint: down push the button off the kivy app window. But for the life of me, when changing the down value to various ints and floats, there was no change. The best approach was to use pos_hint 'y' since we know that the height of the button is 0.49 the top can start at 0.5 upto 0.99 and the bottom can start at 0 upto 0.49.
topHalfLayout = BoxLayout(pos_hint={"y":0.5},size_hint = [1,0.49], orientation = "horizontal")
......
downHalfLayout = BoxLayout(pos_hint={"y":0},size_hint = [1,0.49], orientation = "horizontal")
Another better approach that I used was to create another BoxLayout which holds the top and bottom layouts, allowing the FloatLayout to add the widget as one thing. It provides better spacing in my opinion. 'outside[ [top/bottom] ]outside'
outside = BoxLayout(pos_hint={"top":1},size_hint = [1,2*0.49], orientation = "vertical")
......
topHalfLayout = BoxLayout(size_hint = [1,0.49], orientation = "horizontal")
......
outside.add_widget(topHalfLayout)
......
downHalfLayout = BoxLayout(size_hint = [1,0.49], orientation = "horizontal")
......
outside.add_widget(downHalfLayout)
self.add_widget(outside)

Related

In Kivy programming, where should buttons be created/resized/repositioned in a class created to be used other classes?

#This class is to be used with other classes of widgets, not with the class of kivy.app.
class TempatureFloatLayout(FloatLayout):
tempature = NumericProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_interval(self.update, 1)
self.btn = Button()
self.btn.bind(on_press=self.do_something)
self.add_widget(self.btn)
def do_something(self, self_button):
pass
def update(self, dt):
self.btn.size = self.size # I can only resize and reposition the button here.
self.btn.pos = self.pos # pos and size are assigned with the pos and size
# values of the widget used
... the other codes
class TempatureFloatLayout(FloatLayout) can be used successfully in other classes. The code works correctly as it is. But, When every update, the position and size of the button is to be resized and repositioned. This doesn't feel right to me. How to bind a widget that is used and a button that is used in a different class. Where did I do wrong or is this usage correct?

KivyMD objects are not centered with pos_hint

So in the picture you see that the label and button are at the very end of the screen but i would like that they are both in the middle of their half. Since they are added on a BoxLayout the center of the label has to be on 25% and the center of the icon on 75% on the screens width.
PROBLEM: The label and icon are at edge of window but size_pos should center them
Is the Problem the interaction between kivy Layouts and kivymd objects?
I guess the Problem has to be inside DisconnectPage but just in case I shared the whole GUI part.
I hope someone can help me.
Picture of my problem
CENTERED = {"center_x": 0.5, "center_y": 0.5}
class Main(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.client = self.setup_client()
self.client.start_in_thread()
self.disconnect_card = DisconnectPage(self)
self.lb = MDLabel(text="No message", halign="center")
self.btn = MDFillRoundFlatButton(text="send",on_release = self.send)
self.add_widget(self.lb)
self.add_widget(self.btn)
Clock.schedule_once(self.check_disconnect)
def check_disconnect(self,_):
if self.client.thread.is_alive():
return Clock.schedule_once(self.check_disconnect,1)
self.open_disconnect_screen()
def setup_client(self):
loop = asyncio.get_event_loop()
ip = "127.0.0.1"
port = 1337
client = ClientObject(ip,port,loop,self)
return client
def send(self,_):
self.client.schedule_message("Hello My Friends")
def open_disconnect_screen(self):
self.clear_widgets()
self.add_widget(self.disconnect_card)
def reload(self,_):
print("reloading")
############### SHOWN IN IMAGE ####################
class DisconnectPage(BoxLayout):
def __init__(self, main, **kwargs):
super().__init__(**kwargs)
self.main = main
self.info = MDLabel(text="You disconnected", pos_hint = CENTERED)
self.reconnect_btn = MDIconButton(icon="sync", on_release = self.main.reload, pos_hint = CENTERED)
self.add_widget(self.info)
self.add_widget(self.reconnect_btn)
############ SHOWN IN IMAGE #######################
class MyApp(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
screen = Screen()
main = Main()
screen.add_widget(main)
return screen
app = MyApp()
app.run()
The BoxLayout assigns its horizontal space among its children according to its own rules. So the MDIcon has a default size, and the BoxLayout allocates that space for the MDIconButton. Then all the remaining space is allocated for the MDLabel. The default text positioning (halign) within a Label is left, so you can center the text within the Label by using halign='center'. If you want more control over the positioning and sizing, consider using a FloatLayout.

How to bind the value of a NumericProperty to the Text of a Label?

With Kivy, I understand we can use set the text of a label to a StringProperty() object, so whenever that string is updated, the label will automatically show the updated text.
My minimal example code, works fine, will show "apple" then "banana" one second later:
#test.kv
<MyLabel>:
font_size: 30
text: self.examppleStringProperty
#test.py
class MyLabel(Label):
examppleStringProperty = StringProperty("apple")
def on_kv_post(self, base_widget):
Clock.schedule_interval(lambda dt : self.runlater(), 1)
def runlater(self):
self.examppleStringProperty = "banana"
class TestApp(App):
def build(self): return MyLabel()
Question: How do I do exactly the same, but for a Float? i.e have the label automatically update it's next, whenever the value of the Float is changed?
I have a module that updates a Float value with the current room temperature, which I would just like to show on a label in Kivy, but I'm not sure how to bind it "automagically".
I have tried NumericProperty(), but of course I can't set a label.text to a NumericProperty() object, as it is not a string.
For example, the following code does not work, simply never updates the label text to the number 42, because the NumericProperty isn't bound to the label text in anyway.
class MyLabel(Label):
examppleStringProperty = StringProperty("apple")
exampleNumericProperty = NumericProperty(0)
def on_kv_post(self, base_widget):
self.text = str(self.exampleNumericProperty)
Clock.schedule_interval(lambda dt : self.runlater(), 1)
def runlater(self):
self.exampleNumericProperty = 42
class TestApp(App):
def build(self): return MyLabel()
Just looking for any good way to keep a Label automatically updated with the current value of a Float..
I have found a way to do it (Keep a label.text updated with the value of a Float), by using an "on_NumericProperty" function.
But I would appreciate any advice, if this is good or a bad design - or any suggested alternatives.
class MyLabel(Label):
exampleStringProperty = StringProperty("no data yet")
exampleNumericProperty = NumericProperty(0)
def on_exampleNumericProperty(self, *args):
self.exampleStringProperty = str(self.exampleNumericProperty)
def on_kv_post(self, base_widget):
Clock.schedule_interval(lambda dt : self.runlater(), 3)
def runlater(self):
self.exampleNumericProperty = 42
class TestApp(App):
def build(self): return MyLabel()

Clean way to close splashscreen in kivy after a delay

In Kivy, if you have a ScreenManager with multiple screens already added, you can not change screens with the switch_to function, as that also tries to first add the Screen to the Screenmanager, before switching to it.
Instead, you switch screens simply by setting the current property, like this:
screenmanager.current = "new_screen_name"
The problem is, I wish my SplashScreen to automatically transition to MainScreen after a short delay, using Clock.schedule_once(). But that can only take a function as a parameter, so I I have to write a another function just to change screen, like this:
class MyScreenManager(ScreenManager):
def __init__(self, **kwargs):
super(MainScreenManager, self).__init__(**kwargs)
Clock.schedule_once(switch_to_main_screen, 3)
def switch_to_main_screen(self, *args):
self.current = "main_screen
I'm just wondering if there is a more efficient way to do this? e.g can I somhow directly set the "self.current" property in Clock.schedule_once? Or, generally speaking, is there a better way to do this simple task? (i.e have a parameter be set a few seconds in the future)
You can use the setattr() python built-in. Try replacing:
Clock.schedule_once(switch_to_main_screen, 3)
with:
Clock.schedule_once(lambda dt: setattr(self, 'current', 'main_screen'), 3)
See the documentation.
Consider this, its my ready sheet in kivy:
import #what u need
Builder.load_file('the.kv')
class fscreen(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class secscreen(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
pass
class thscreen(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
pass
class theapp(App):
def build(self):
self.screenm = ScreenManager()
self.fscreen = fscreen() ## as default this is my
screen = Screen(name = "first screen") # my first screen
screen.add_widget(self.fscreen) # if i want second screen
self.screenm.add_widget(screen) # as first i move this below
## secscreen its waay
self.secscreen = secscreen() ### efficient
screen = Screen(name = "secondscreen")
screen.add_widget(self.secscreen)
self.screenm.add_widget(screen)
self.thscreen = thscreen()
screen = Screen(name = "thirdscreen")
screen.add_widget(self.thscreen)
self.screenm.add_widget(screen)
return self.screenm
if __name__ == "__main__":
theapp = theapp()
theapp.run()
This way the fscreen is by default the first screen after the presplash

Kivy Popup or Eventloop Interaction?

The first screen of my app has a small menu (in a gridlayout) of three buttons. Two are supposed to open popups. One for Help and one for About.
The third one changes to another screen.
Only one popup works. The first one called (in the kivy file) works, the second doesn't open the popup. If I switch the order in cdd.kv, then the other one works.
Excerpt from cdd.kv:
CDDMainMenuLayout:
HelpButton:
size_hint: .5,.5
MetadataButton:
size_hint: .5,.5
on_release: app.root.current = 'metadata'
AboutButton:
size_hint: .5,.5
Excerpt from main.py:
class CDDMainMenuLayout(GridLayout):
"""
Provides the layout for the three buttons on the home screen.
"""
def __init__(self, *args, **kwargs):
super(CDDMainMenuLayout, self).__init__(*args, **kwargs)
self.rows = 1
self.cols = 3
self.size_hint = (.5,.5)
...
class CDDButton(Button):
def __init__(self, **kwargs):
super(CDDButton, self).__init__(**kwargs)
self.text = _('Button')
self.background_color = colors.grey2
class AboutButton(CDDButton):
def __init__(self, **kwargs):
super(AboutButton, self).__init__(**kwargs)
self.text = _("About the CDD")
self.background_color = colors.red1
a = Popup()
a.title = _("About Constraint Definition Designer, Version - " + __version__)
a.content = RstDocument(source='about.rst')
a.size_hint_x = .8
a.size_hint_y = .8
self.bind(on_release=a.open)
class HelpButton(CDDButton):
def __init__(self, **kwargs):
super(HelpButton, self).__init__(**kwargs)
self.text = _("Help")
self.background_color = colors.green1
h = Popup()
h.title = _("CDD Help")
h.content = RstDocument(source='help.rst')
h.size_hint_x = .8
h.size_hint_y = .8
self.bind(on_release=h.open)
Does anything change if you add extra lines self.popup = h and self.popup = a? One possibility is that your popups are simply being garbage collected since you don't store any references to them. I'm not sure if/how this would give your particular behaviour, but it's worth a try.

Categories

Resources