I am new to python and kivy, I am trying to clear all widgets in a host when any button is pressed, my code is as follows:
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.label import Label
spots={}
class spot(Button):
'''
classdocs
'''
def __init__(self, **kwargs):
'''
Constructor
'''
super(spot,self).__init__(**kwargs)
self.ismine=False
if 'text' in kwargs:
self.text=kwargs['text']
else:
self.text="X"
def on_press(self, *args, **kwargs):
#return Button.on_press(self, *args, **kwargs)
game.spottouched(self, self.text)
class game(BoxLayout):
def __init__(self,**kwargs):
super(game,self).__init__(**kwargs)
self.m=minesholder(rows=25, cols=25)
self.add_widget(self.m)
self.attachtogrid()
def attachtogrid(self):
self.m.clear_widgets()
spots.clear()
for r in range(0,25):
for c in range(0,25):
idd=str(r)+","+str(c)
spots[idd]=idd
self.m.add_widget(spot(text=idd))
def spottouched(self,spotid):
#popup=Popup(title=spotid)
#popup.open()
self.clear_widgets()
The last line is for clearing the widgets, and spot class has the on_press event. I am trying to pass the event of on_press even of button on app to a holding boxlayout (game class), could you please tell/point me in right direction to lean on how pass the events?
Thank you.
I think this is what you want, based on your comment on #inclement's answer: Get rid of the on_press method on your button. You want to bind to the event instead - this will work more like WPF.
for r in range(0,25):
for c in range(0,25):
idd=str(r)+","+str(c)
spots[idd]=idd
s = spot(text=idd)
self.m.add_widget(s)
s.bind(on_press=self.spottouched)
So self.spottouched is like an event handler for the spots on_press event.
s.bind(on_press=self.spottouched)
is kind of like this:
AddHandler s.Click, AddressOf spottouched
Note that adding the handler this way, the handler will receive a single argument which is the spot instance. You can get spot.text from that instance.
game.spottouched(self, self.text)
You've misunderstood how python classes work. You want to clear the widgets from the instance of your game that you're actually using - something you created by writing game(). With a reference to that, you need to do instance.spottouched(spotid) to call the method of that instance.
What you're doing instead is calling the method via the class definition, which really makes no sense here and you don't want to do it. Actually, you basically never want to do this.
Anyway, the way to fix this depends on how you actually constructed your app, but it boils down to you need to keep a reference to the game instance somewhere that the spot instance can see it. The best way depends on the rest of your code, but you can always do it via the app class, e.g.
App.get_running_app().game = game()
and later
App.get_running_app().game.spottouched(self.text)
This is just an example, it might be a bad way to do things sometimes.
Also, begin your widget names with capital letters! kv language uses this to identify them, you will hit problems using it if you don't name them this way. It's also a strong python convention worth following.
Related
for a python program I created a gui with QtDesigner. Inside my program the gui is initiated and calls the .ui-file to implement content.
The gui class object is than given to a Controller, and here it gets tricky:
Instead of one Controller, there are a main controller and some sub-controller for different parts of the gui. The main Controller does one or two general things and than hands over different parts of the gui to different sub controller.
See the following example for better understanding:
import Controller # import the folder with the controller
def __init__(self, window):
self.window = window # self.window is the handed over gui
self.sub_controls()
def sub_controls(self):
Controller.Sub_Controller_1(window = self.window.part_1)
Controller.Sub_Controller_2(window = self.window.part_2)
Controller.Sub_Controller_3(window = self.window.part_3)
...
The sub-Controller is set up like this:
def __init__(self, window):
self.window = window
... # do stuff:
-----------------------------------------
by trying to call a widget in this Sub-Controller (lets say its a label called timmy), i get an error:
self.window.timmy.setVisible(False)
AttributeError: 'QWidget' object has no attribute 'timmy'
but by using the children()-Method, which returns a list of all children in the gui, I may access this label:
self.window.children()[1].setVisible(False)
This works well and hides timmy.
By trying to do this in the main Controller, it works fine as usual:
self.window.timmy.setVisible(False) # works fine here
I also tried to save the sub-controller object like this:
def sub_controls(self):
self.save_part_1 = Controller.Sub_Controller_1(window = self.window.part_1)
but this doesn't work.
Does any one have a suggestion, how I could solve this Problem?
Sure, I couldt access just all widgets with the children()-method-list, but this is laborious because of the great amount of widgets. Same thing applies to reassigning every child.
PS:
Unfortunately I cannot show you the original Code due to company guidelines.
Ok so I figured out, that the tree Structure of in each other nested widgets has nothing to do with the naming of the widgets. For example:
MainWindow
->centralWidget
->otherWidget
->Button
In this you can address "Button" with self.Button, because (how I believe) the name is saved in MainWindow-Level. If you cut out "otherWidget", the QPushButton in it still exists, but cannot addressed by name.
So it's not possible to just hand over a part of your Gui to a Handler, at least if it's build with QtDesigner.
My Solution for me to the original Problem is to hand over the complete Gui to every sub handler:
def __init__(self, window):
self.window = window # self.window is the handed over gui
self.sub_controls()
def sub_controls(self):
Controller.Sub_Controller_1(window = self.window)
Controller.Sub_Controller_2(window = self.window)
# Controller.Sub_Controller_3(window = self.window.part_3) # before
I am trying to realize a dynamic TreeView where all updates are initialized in kivy and call a function called populate_tree_view(self, tree). The available Tree-View-docs have been a bit cryptic to me regarding this approach...I already fail at filling a TreeView on init of the App. For the following code I get the error:
name "wid" is not defined
How is that possible? As far as I understand, I refer to self=Widget, and this widget has a TreeView called "wid". Please help me.
My kivi file:
<Widget>
TreeView:
id: wid
root_options: dict(text=somename)
my python code:
class Widget(StackLayout):
def populate_tree_view(self, tree):
self.wid.add_node(TreeViewLabel(text='My first item'))
print("done")
# Init GUI
class App (App):
def build(self):
App = Widget()
App.populate_tree_view(tree)
return App
App().run()
Several issues:
You need to use Clock.schedule_once() to call your populate_tree_view() method. That will delay the call until the wid is is available in the ids. Put the Clock.schedule_once() in the build() method just before the return.
To access wid you must use the ids dictionary (self.ids.wid.add_node(TreeViewLabel(text='My first item'))
App is a class in the Kivy package. Redefining it as a Widget instance (or even as the name of your App class) is a bad idea. Just don't set App = to anything and don't use class App():.
I should probably start by saying I am an absolute beginner with Python and Kivy, so please excuse me if the question is very naive.
I am trying to get a Kivy button to run a command on_press. The command is defined in another function which belongs to the same class. However, I always get the following error:
self.searchButton.bind(on_press=self.searchRecipe())
File "kivy_event.pyx", line 419, in kivy._event.EventDispatcher.bind
AssertionError: None is not callable
I have already looked at similar questions (here and here) but still could not figure out what I am doing wrong.
I should also add that, for the sake of learning, I do not want to use .kv files at the moment, but include everything in my python script. After getting a nice understanding of it, I will try to migrate some parts to a .kv file.
Below is a minimal (non-working) example. Of course the searchRecipe function should do more than just printing (connect to a SQLite db and perform some other stuff), but this is just a tryout.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class RootWindow(BoxLayout):
def __init__(self, **kwargs):
super(RootWindow, self).__init__(**kwargs)
self.orientation = 'vertical'
self.searchButton = Button(text="Search")
self.searchButton.bind(on_press=self.searchRecipe())
self.add_widget(self.searchButton)
def searchRecipe(self):
print("It works!")
class RecipApp(App):
def build(self):
return RootWindow()
if __name__ == "__main__":
RecipApp().run()
self.searchButton.bind(on_press=self.searchRecipe())
self.searchRecipe() evaluates to None. You want self.searchButton.bind(on_press=self.searchRecipe).
I'm a beginner Kivy developer and I need some advice from you guys.
I'm using ScreenManager to jump between screens and as far as I noticed, all the screens are initialized just after the application starts, and I need them to be initialized with certain attributes from previous screens, like, selecting the category or stuff. Is there any way to do that?
I have two buttons in CategorySelectScreen both representing certain category, I want them to send a string attribute to DictScreen, where it will be used as an argument in CategorySelect method, which filters the items list, but the thing is, the application need that argument on start and without it the interpreter would just throw errors.
Also, I think I'm using kivy in a very bad way, could you please look into my code and give me some pro tips? Thanks in advance, cheers :)
kv file: http://pastebin.com/UdvGS7Wv
py files: http://pastebin.com/gJn9Mrip
When declaring your screens decide what object would be it's input. Then make this object a property. After that, setup on_... callback where you build your screen with widgets with values based on this input object. For example:
class DictScreen(Screen):
category_selected = ObjectProperty(None)
def on_category_selected(self, instance, value):
category_selected = value
self.clear_widgets()
self.add_widget(Button(text=category_selected.name))
And in previous screen, before you switch to DictScreen get its instance from app.root.ids, then assign category_selected to it and then set new current screen with ScreenManager. This way your DictScreen will be immediately build with choosen category right before you switch to it.
"before you switch to DictScreen get its instance" how this can be done? It's well explained here:
https://kivy.org/docs/api-kivy.uix.widget.html?highlight=widget#kivy.uix.widget.Widget.ids
I've created a ScreenManager, and created several Screen instances for that ScreenManager.
I'd like each Screen to display a GridLayout class. For example, let's say you have:
class MainScreen(Screen):
...
class MainLayout(GridLayout):
...
When MainScreen is the active screen, I'd like MainLayout to be shown.
Is there a way to do this purely in python (i.e. without markup)? Thank you.
Is there a way to do this purely in python (i.e. without markup)? Thank you.
You never need to use kivy language (I assume that's what you mean by markup), though it's highly recommended where possible because it makes lots of stuff easier.
Nonetheless, to actually answer your question, all you have to do is add your gridlayout widget to your screen widget, something like
mainscreen = MainScreen()
mainlayout = MainLayout()
mainscreen.add_widget(mainlayout)
Then when you set the current screen in your screenmanager to be mainscreen, you should see the GridLayout.
Edit: In case it's unclear, this is in general the way you add widgets to other widgets. When you see an example in kv language like
<MyScreen>:
GridLayout:
...
...ultimately that gets translated to something much like the above example code - an instance of MyScreen is created and a GridLayout is added to it with add_widget.