Error:
TypeError: changetxt() takes no arguments but 2 given, or global name play_btn not defined.
I tried adding instance, self as well into the def args but still have the same error.
import kivy
from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.lang import Builder
class launchScreenMenu(FloatLayout):
def __init__(self, **kwargs):
super(launchScreenMenu, self).__init__(**kwargs)
menuanchor = AnchorLayout(anchor_x='left',anchor_y='bottom')
menu = StackLayout(orientation='bt-lr',size_hint=(0.5,1))
about_btn = Button(text='About',size_hint=(0.3,0.1))
help_btn = Button(text='Settings',size_hint=(0.3,0.1))
settings_btn = Button(text='Help',size_hint=(0.3,0.1))
menu.add_widget(about_btn)
menu.add_widget(help_btn)
menu.add_widget(settings_btn)
menuanchor.add_widget(menu)
return self.add_widget(menuanchor)
class launchScreenBtn(AnchorLayout):
def __init__(self, **kwargs):
super(launchScreenBtn, self).__init__(**kwargs)
play_btn = Button(text="Play")
self.anchor_x = 'center'
self.anchor_y = 'center'
self.size_hint = 0.2,0.2
self.add_widget(play_btn)
play_btn.bind(on_press=self.changetxt)
def changetxt():
play_btn.text = 'Game Over'
class GameApp(App):
def build(self):
root = AnchorLayout()
root.add_widget(launchScreenMenu())
root.add_widget(launchScreenBtn())
return root
if __name__=='__main__':
GameApp().run()
All instance methods should always has self as their first argument:
def changetxt(self, *args):
self.play_btn = 'Game Over'
*args is just to be secure, in case it wasn't you who pass the argument.
Also, change all play_btn inside the class to self.play_btn:
self.play_btn = Button(text="Play")
Well, hope this helps!
The first argument of instance methods (think any function in a class, but there are exceptions) is a link to the instance itself. By convention this is called self. Reference
I don't know with 100% certainty what your second argument is. It may be play_btn since that's what's calling the function. That would be convenient since you're trying to reference that anyway, and it otherwise would be undefined.
How you want your def to read is like this:
def changetxt(self, play_btn):
Related
I have spent a fair amount of time trying to fix this,
my actual goal is to run a forloop to generate a list in kivymd and while doing so I got this error?
TypeError: __init__() takes 1 positional argument but 2 were given
this is the code that I have simplified
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.list import *
KV = '''
ScrollView:
MDList:
id: list
'''
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
ib = IconLeftWidget(icon='github')
ibn = OneLineAvatarIconListItem(
IconLeftWidget(icon='github')
)
self.root.ids.list.add_widget(ib)
MainApp().run()
need a generate a list via a forloop and use the icon as a button
OneLineAvatarIconListItem( IconLeftWidget(icon='github') )# error line
I know that the problem is that you can only pass one argument for the function but there got a be a way right!
please help T-T
I don't know if it can be done in one line but this works for me
#ibn = OneLineAvatarIconListItem(text='Hello')
ibn = OneLineAvatarIconListItem()
ibn.add_widget(IconLeftWidget(icon='plus'))
ibn.add_widget(IconRightWidget(icon='minus'))
self.root.ids.list.add_widget(ibn)
You could create own widget to make it simpler
class MyItem(OneLineAvatarIconListItem):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add_widget(IconLeftWidget(icon='plus'))
self.add_widget(IconRightWidget(icon='minus'))
# ... code ...
#my_item = MyItem(text='World')
my_item = MyItem()
self.root.ids.list.add_widget(my_item)
Minimal working code
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.list import *
KV = '''
ScrollView:
MDList:
id: list
'''
class MyItem(OneLineAvatarIconListItem):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add_widget(IconLeftWidget(icon='plus'))
self.add_widget(IconRightWidget(icon='minus'))
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
ib = IconLeftWidget(icon='github')
self.root.ids.list.add_widget(ib)
ibn = OneLineAvatarIconListItem(text="Hello", bg_color="lime")
ibn.add_widget(IconLeftWidget(icon='plus'))
ibn.add_widget(IconRightWidget(icon='minus'))
self.root.ids.list.add_widget(ibn)
my_item = MyItem(text="World", bg_color="red")
self.root.ids.list.add_widget(my_item)
MainApp().run()
I am trying to use kivy.clock object more specifically Clock.schedule_interval to take temperature readings every couple seconds.
I create a method in my main screen class (MyTerrLayout(Screen)) called clockAction. now when I type in Clock.schedule_interval(clockAction, 2) I get:
NameError: clockAction is not defined
So I tired to do self.clockAction but that didn't work either. I tried various methods to get it going like moving Clock.schedule_interval(clockAction, 2) to its own class but I get other errors. Do i have to create an instance of the method clockAction? Or since Clock.schedule_interval(clockAction, 2) might be a class attribute it needs to be called a different way.
Or is it because Clock.schedule_interval(clockAction, 2) is a class attribute that I need to call clockAction a different way? I can't seem to make the connection.
If someone could point me in the right direction that would be awesome. Also does anyone know where I can practice more complex examples of OOP and class manipulation? Or does someone have some old homework regarding class manipulation and OOP concepts?
Here is my code
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.properties import ObjectProperty
from random import randint
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
import threading
Window.size = (600,250)
tep = 75
hum = 85
# Window Manager Class
class WindowManager(ScreenManager):
pass
# Main home menu Class
class MyTerrLayout(Screen):
internal_read = ObjectProperty(None)
external_read = ObjectProperty(None)
target_temp = ObjectProperty(None)
target_hum = ObjectProperty(None)
Clock.schedule_interval(clockAction, 2) #Clock Schedule
def set_temp(self):
global tep
self.ids._Target_Temp.text = str(tep) + " F"
def set_hum(self):
global hum
self.ids._Target_Hum.text = str(hum) + "%"
def clockAction(self, dt=0): ####method I am trying to run
self.ids.external_reading.text = str(randint(1,10))
print('test')
# Temprature Screen Class / Window
class TempWindow(Screen):
def sub_temp(self):
global tep
tep = tep - 1
self.temp_menu.text = str(tep)
def add_temp(self):
global tep
tep = tep + 1
self.temp_menu.text = str(tep)
def set_temp(self):
terrlayout = self.manager.get_screen('main')
terrlayout.set_temp()
class HumWindow(Screen):
def sub_hum(self):
global hum
hum = hum - 1
self.ids.set_hum_menu.text = str(hum)
def add_hum(self):
global hum
hum = hum + 1
self.ids.set_hum_menu.text = str(hum)
def set_temp(self):
terrlayout = self.manager.get_screen('main')
terrlayout.set_hum()
class AC(Screen):
def build(self):
Clock.schedule_interval(self.Callback_Clock, 2)
def Callback_Clock(self, dt):
terrlayout = self.manager.get_screen('main')
terrlayout.clockAction()
# Builder Section
kv = Builder.load_file('terrlayoutV1.kv')
class TerrLayout(App):
def build(self):
return kv
if __name__=="__main__":
TerrLayout().run()
The problem is here:
class MyTerrLayout(Screen):
[...]
Clock.schedule_interval(clockAction, 2)
[...]
That code is in the class part of MyTerrLayout. In other languages, we call that static. clockAction has self as a parameter, which means it is not static.
In a static context, you can't access members of an object, because there is no object to refer to.
IMHO this should be done when the object is created:
def __init__(self, **kw):
super().__init__(**kw)
Clock.schedule_interval(self.clockAction, 2) # Clock Schedule
Note that the __init__ method has self and you can refer to self.clockAction.
I got two classes that have two methods that I want to be able to access from different classes, so I inherited from App in both classes to use get_running_app with kivy properties (hope you understand what I'm saying). Now I have a problem because when I try calling App.get_running_app().method(), it only looks for the method in the first class, but the method is in the second class. I know that inheriting from App in two classes isn't really good but that was at the time the easiest way because I didn't have to call get_running_app for both classes. I had like the main class (where the def build is) and one just for main screen and I needed to call a method in main screen class. Now I have that method and the method in the main class.
So my question is, is there any way of distinguishing two classes with get_running_app()? Or could you give me some tips on how to solve this problem in different way if you have some idea?
Here is the code like you asked, just forget what this code does, I simplified it like this just to show the core problem and the error it doesn't matter what does.
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.properties import ObjectProperty
from kivy.properties import NumericProperty
class GameWidget(Screen, App):
num = NumericProperty(0)
change_value = ObjectProperty(None)
def __init__(self, **kwargs):
super(Screen, self).__init__(**kwargs)
self.num = 0
self.button = Button(text="go back", size_hint=(0.2, 0.2), pos_hint={'x': 0.4, 'y':0.4},
on_press=lambda x: self.go_main_screen())
self.add_widget(self.button)
self.label = Label(text=str(self.num), size_hint=(0.2, 0.2), pos_hint={'x':0.2, 'y':0.2})
self.add_widget(self.label)
def change_value(self):
self.num += 1
self.label.text = str(self.num)
def go_main_screen(self):
self.manager.transition.direction = 'right'
self.manager.current = 'main'
class Main_screen(Screen):
def __init__(self, **kwargs):
super(Main_screen, self).__init__(**kwargs)
self.button = Button(text='click me', size_hint=(0.2, 0.2), pos_hint={'x': 0.4, 'y': 0.4},
on_release=lambda x: self.call_methods())
self.button2 = Button(text='go to next screen', size_hint=(0.2, 0.2), pos_hint={'x':0.4, 'y':0.6},
on_release=lambda x: self.ch_screen())
self.add_widget(self.button)
self.add_widget(self.button2)
def ch_screen(self):
self.manager.transition.direction = 'left'
self.manager.current = 'game'
def call_methods(self):
App.get_running_app().change_value()
App.get_running_app().print_some_stuff()
class Manager(ScreenManager):
pass
class MyApp(App):
print_some_stuff = ObjectProperty(None)
def build(self):
sm = Manager()
sm.add_widget(Main_screen(name='main'))
sm.add_widget(GameWidget(name='game'))
return sm
def print_some_stuff(self):
print('you clicked')
MyApp().run()
The App.get_running_app() method will always return the same object, which should be the currently running App. Since you can only have one running App, that should always be the MyApp instance in your code. However, I believe you have uncovered a bug in the App code, because App.get_running_app() actually returns the last created App, not the currently running App. So, in your code, it will always return the GameWidget instance. That bug will not affect a correctly structured code.
The following problems/solutions appear in your code:
You should not have more than one App class in your App. Change:
class GameWidget(Screen, App):
to:
class GameWidget(Screen):
You can access Screens using the get_screen() method of the ScreenManager, which is your App root. So you can chaange:
App.get_running_app().change_value()
to:
App.get_running_app().root.get_screen('game').change_value()
Im writing an app in Kivy which automaticaly adds Buttons and gives them a unique id using a for loop. This id is then used as a key in the dictionary for a link. So the dictionary works fine and after printing it, it outputs {'button0': 'somewebsite', 'button1': 'other website', 'button2': 'andanotherwebsite'} which is exactly what I want but the button callback function always prints out button2 instead of its own id. Am I assigning the ids wrong? The example below demonstrates my problem.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivymd.utils import asynckivy
from kivy.clock import Clock
class TestButton(Button):
def callback(self):
print(self.id)
class RootWidget(BoxLayout):
def __init__(self):
super().__init__()
self.links = ["somewebsite", "other website", "andanotherwebsite"]
self.dic_btn_to_lnk = {}
self.size_hint = (None, None)
self.size = ("600dp", "50dp")
Clock.schedule_once(self.add_widgets, 0)
def add_widgets(self, *args):
async def update():
number = 0
for link in self.links:
button = TestButton()
button.text = link
button.size = ("200dp", "50dp")
button.pos_hint = {"center_x": .5}
btn_id = "button" + str(number)
button.id = btn_id
button.bind(on_release=lambda x: button.callback())
number += 1
self.dic_btn_to_lnk[btn_id] = link
self.add_widget(button)
print(self.dic_btn_to_lnk)
asynckivy.start(update())
class TestApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
TestApp().run()
The problem is that your on_release binding is calling button.callback(), and button will be the last Button added by the time the on_release is triggered. The solution is to use partial, which freezes its arguments to their values when partial is executed, so the on_release calls the correct button.callback. Like this:
button.bind(on_release=partial(button.callback))
And to simplify the above, the definition of callback is changed to:
class TestButton(Button):
def callback(self, instance):
print(self.id)
I'm making a label that should update every second or so. I tried making it with the clock schedule but it doesn't seem to work. The weird is if I use a button to call the same function it works fine.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.clock import Clock
class FirstLayout(BoxLayout):
r = 0
def __init__(self, **kwargs):
super(FirstLayout, self).__init__(**kwargs)
self.change = self.ids.temp_label
def my_callback(self, *args):
self.r += 1
print self.r
t = str(self.r)
self.change.text = t
class TryApp(App):
def build(self):
Clock.schedule_interval(FirstLayout().my_callback, 1)
return FirstLayout()
app = TryApp()
app.run()
the .kv file:
<FirstLayout>:
orientation: 'vertical'
Label:
id: temp_label
text: 'something'
Button:
on_press: root.my_callback()
When I run the code I get the prints showing that the function is running but the label doesn't update. Anything wrong with my logic there?
Thank you in advance.
PS: I know there are several questions about this here, sadly those I found were replied with answers about using the Clock which I already do
The problem is that the callback is for an instance that you don't use:
def build(self):
Clock.schedule_interval(FirstLayout().my_callback, 1) #<--- FirstLayout created and never used
return FirstLayout() #this one will be used :(
Instead, You need to call the method of the FirstLayout that you are using
def build(self):
first_layout = FirstLayout() # "There should be one ..." :)
Clock.schedule_interval(first_layout.my_callback, 1)
return first_layout