I was trying to register all TextInputs and Spinner in my GUI.
Each of these does have a variable gid.
The problem with the root_widget.walk() method is that I have different tabs and it does not load the widgets that have not been displayed yet.
However this is not my biggest problem.
The thing is that the walk()method does only show Widgets and not TextInputs nor Spinner.
My question now is: How do you traverse EVERY object (TextInputs/Spinner etc.) including those that have not been displayed yet (in a different tab)
I am very happy for any kind of help or advice.
Greetings, Finn
The walk() widget method does visit all child widgets (including TextInput and Spinner. If you are using TabbedPanel as your root widget, then the following will register all your widgets (if it is called by a binding to on_draw):
registered = False
def on_draw(*args):
global registered
if registered:
return # just to avoid running this many times
registered = True
app = App.get_running_app()
for tab in root_widget.tab_list: # assumes root_widget is a TabbedPanel
if tab.content is not None:
for widget in tab.content.walk():
app.register_widget(widget)
A similar construct could be done if you are using a ScreenManager as your root widget.
Related
I'm using Kivy and I'm trying to setup a ScreenManager, but I don't want that ScreenManager to be the root widget in my window. Here's a test code snippet that demonstrates what I'm trying to do. (This code demonstrates my problem.)
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
class MyRootWidget(Widget):
def __init__(self, **kwargs):
super(MyRootWidget, self).__init__(**kwargs)
self.screen_manager = ScreenManager()
self.add_widget(self.screen_manager)
# self.add_widget(Label(text='label direct'))
class MyApp(App):
def build(self):
root = MyRootWidget()
new_screen = Screen(name='foo')
new_screen.add_widget(Label(text='foo screen'))
root.screen_manager.add_widget(new_screen)
root.screen_manager.current = 'foo'
for x in root.walk():
print x
return root
if __name__ == '__main__':
MyApp().run()
When I run this code, the window is blank, though I would expect that it would show the text "foo screen"?
The print of the walk() command shows that the root widget contains the screenmanager, my screen, and the label, like this:
<__main__.MyRootWidget object at 0x109d59c80>
<kivy.uix.screenmanager.ScreenManager object at 0x109eb4ae0>
<Screen name='foo'>
<kivy.uix.label.Label object at 0x109ecd390>
So that's working as I would expect.
If I uncomment the line which adds the label widget directly to the root widget, that label shows up as expected.
Also if I change the MyApp.build() method so that it returns new_screen instead of returning root, it also works in that I see the label "foo screen" on the display.
BTW, the reason I want to not have the screen manager be the root widget is because I want to be able to print messages (almost like popups) on the screen in front of whichever screen is active (even if screens are in the process of transitioning), so I was thinking I would need the screen manager not to be root in order to do that?
Also my ultimate goal is to have multiple "quadrants" in the window, each with its own screen manager, so I was thinking I needed to make sure I can show screen managers that are not the root widget in order to do this.
So if there's a better way for me to do this, please let me know. Also I do not want to use .kv files for this as I want to set this entire environment up programmatically based on other config options (which I've left out of this example.)
Overall though I wonder if anyone knows why this code doesn't work?
Thanks!
Brian
The problem is you are sticking your ScreenManager in a generic Widget. If you put it in a Layout, it will display properly, ie:
class MyRootWidget(BoxLayout):
There are several layouts available: http://kivy.org/docs/gettingstarted/layouts.html
Follow up from Kivy class in .py and .kv interaction , but more complex.
Here is the full code of what I'm writing:
The data/screens/learnkanji_want.kv has how I want the code to be, but I don't fully understand how the class KanjiOriginScreen() plays it's role in screen management.
data/screens/learnkanji.kv works how I want it, but for this to work I have to put keyb_height in class KanjiOriginScreen() (main.py). However I want that code to be in the class LayoutFunction() (learnkanji.py).
Question
How can I put keyb_height in the function LayoutFunction() and access this in the .kv file in <LayoutFunction>?
Could you also explain why KanjiOriginScreen: can be put in learnkanji.kv without < > and the program still recognizes it should use this?
If anything is unclear, please ask :)
Edit
I found out that I didn't import the learnkanji.py in the learnkanji.kv file and that caused that it couldn't find the class 'LayoutFunction'.
#:import learnkanji data.screens.learnkanji
To answer your questions:
The way you are doing it should work. You should be able to access object attributes from kv. If your attribute is going to change, however, and you want the UI to update when it does, you should use Kivy Properties. If it is constant, a normal attribute works fine.
From the Kivy Docs, <Widget>: is a class rule that will be applied to every instance of that class. Widget: will create an actual instance of that class (in this case it is your root widget).
As for ScreenManager and Screens, you can think of them this way. Each Screen is it's own individual UI (it's own root widget). The screen manager is a container that holds your Screen and can swap between different Screens. This lets you create separate UIs that you can toggle between. Each UI is a separate widget tree with a Screen at its root. The docs are actually pretty good at describing ScreenManager.
How can I put keyb_height in the function LayoutFunction() and access this in the .kv file in ?
You can't do this with a function. You need to make LayoutFunction into a class to do this. Like so:
main.py
class LayoutClass(BoxLayout): # I made it a boxlayout, you could make it anything you want
keyb_height = NumericProperty(260) # from kivy.properties import NumericProperty
kv file:
<LayoutClass>: # can only access it this way if it's a class in main.py
something: root.keyb_height
Could you also explain why KanjiOriginScreen: can be put in learnkanji.kv without < > and the program still recognizes it should use this?
It sounds like you're asking how you can achieve this.. but I can't think why?
Unless you want it managed by a ScreenManager perhaps? However, the only way you can have KanjiOriginScreen within the kv file without the <> is if it is inside another root widget. For instance, see Testy and ScreenTwo as they are in the kv file under <Manager> in my answer to your other question(here). They are without <> because they are class instances, WITHIN another class(the Manager class). Only root widgets have the <> around them in the kv file. If none of this makes sense to you, you need to do a tutorial on kivy.
Check out this tutorial I made a while back, it explains a little about root widgets in kv(at around 4.30).
Sorry I was not clear with my question, but with the help on IRC on #Kivy I ended up with the following:
learnkanji.py
class LayoutFunctioning(BoxLayout):
keyb_height = NumericProperty(260)
learnkanji.kv
KanjiOriginScreen:
name: 'LearnKanji'
fullscreen: True
LayoutFunction:
id: lfunc
#...code...
height: lfunc.keyb_height #Instead of root.keyb_height
Now I understand how to use the id, I can use lfunc to call my code in LayoutFunction() :)
I'm trying to write a very basic Kivy program that will use 3 different layouts to split the screen into :
a header (at the top of the screen)
a text zone (in the middle of the screen)
a console (at the bottom of the screen)
So far I was thinking to use a main gridLayout, in which I use 3 different floatLayout.
Here's what the code looks like:
class Logo(App):
def build(self):
layout = GridLayout(rows=3)
layoutTop = FloatLayout(size=(100,300))
layoutMid = FloatLayout(size=(100,300))
layoutDown = FloatLayout(size=(100,300))
logo = Image(source='imagine.png',size_hint=(.25,.25),pos=(30,380))
blank = Label(text='', font_size = '25sp',pos=(-200,100))
titre = Label(text='#LeCubeMedia',font_size='40sp',pos=(0,280))
ip = Label(text='192.168.42.1',font_size='25sp',pos=(250,280))
layoutTop.add_widget(titre)
layoutTop.add_widget(logo)
layoutTop.add_widget(ip)
layoutMid.add_widget(blank)
layout.add_widget(layoutTop)
layout.add_widget(layoutMid)
return layout
if __name__ == '__main__':
Logo().run()
Actually my problem is regarding the creation of the console. I have read a lot of the Kivy docs, but I am still looking for a good way to do this type of widget.
How do you think it would be if I send something with a Python print into my Kivy app, and then refresh as soon as I need to send something else (to erase the previous print). This way it would be a console-like. But, so far I have not much ideas..
Any ideas ?
I have seen 2 types of consoles in Kivy. The first is a multiline textinput in a scrollview where you append the new text to the old in the textinput. The second is a BoxLayout or GridLayout in a Scrollview where each console output is a separate label in the layout.
This was a attempt trying stuff out with kivy, the code is old and you might need to adjust it a bit to make it run with latest kivy. Kivy-designer also includes this. This is using the simple way of using two textinputs, 1 for history and the other for input.
A better way to do a proper console would be to use pyte and draw characters directly onto the canvas of a widget. This way one would get VT emulation for free.
Image: http://dl.dropbox.com/u/4900888/GUI.png
Source and UI is in comments, due to "New-user-limiations"
I'm sorry if I've totally misunderstood what this site is about, but I have a problem.
When the button in the lower left corner is pressed, I want to get the values from the three spinboxes and save the as variables for use in a function that is triggered by the button.
I'm really blank as to how you would do this.
Any help is appreciated, and additional information can be supplied if it needs to.
EDIT:
I am using Python, and Gtk+ through Glade.
PS:
Does Stack Overflow have any code sharing site prefferences such as pastebin and so on?
The short answer is: with the spin button's get_value_as_int() method.
I suspect, however, that your actual problem is getting a reference to the spin button to call get_value_as_int() on. From your code I see that you are using gtk.Builder to build your UI.
Accessing widgets
Keeping a reference to specific widgets in Handler instance:
handler = Handler()
builder = Gtk.Builder()
...
# Store references to widgets in `handler`
for widget_name in ('sbtn_days', 'sbtn_hours', 'sbtn_minutes'):
setattr(handler, widget_name, builder.get_object(widget_name))
# The above is equivalent to the following:
handler.sbtn_days = builder.get_object('sbtn_days')
...
# In signal handling code:
days = self.sbtn_days.get_value_as_int()
Or you can keep a reference in to builder in your Handler instance:
builder = Gtk.Builder()
handler = Handler()
handler.builder = builder
...
# In signal handler code:
sbtn_days = self.builder.get_object('sbtn_days')
days = sbtn_days.get_value_as_int()
Notes
In the code above,
I assumed that your spin button for days is named sbtn_days. Adjust as necessary.
I only demonstrated accessing sbtn_days and its value. The other buttons can be accessed in a similar way.
P.S. There are a bunch of other problems with your code keeping it from being "good".
The problem I'm running into here is that, when I click on the different file names in the Listbox, the Label changes value one click behind whatever I'm currently clicking on.
What am I missing here?
import Tkinter as tk
class TkTest:
def __init__(self, master):
self.fraMain = tk.Frame(master)
self.fraMain.pack()
# Set up a list box containing all the paths to choose from
self.lstPaths = tk.Listbox(self.fraMain)
paths = [
'/path/file1',
'/path/file2',
'/path/file3',
]
for path in paths:
self.lstPaths.insert(tk.END, path)
self.lstPaths.bind('<Button-1>', self.update_label)
self.lstPaths.pack()
self.currentpath = tk.StringVar()
self.lblCurrentPath = tk.Label(self.fraMain, textvariable=self.currentpath)
self.lblCurrentPath.pack()
def update_label(self, event):
print self.lstPaths.get(tk.ACTIVE),
print self.lstPaths.curselection()
self.currentpath.set(self.lstPaths.get(tk.ACTIVE))
root = tk.Tk()
app = TkTest(root)
root.mainloop()
The problem has to do with the fundamental design of Tk. The short version is, bindings on specific widgets fire before the default class bindings for a widget. It is in the class bindings that the selection of a listbox is changed. This is exactly what you observe -- you are seeing the selection before the current click.
The best solution is to bind to the virtual event <<ListboxSelect>> which is fired after the selection has changed. Other solutions (unique to Tk and what gives it some of its incredible power and flexibility) is to modify the order that the bindings are applied. This involves either moving the widget bindtag after the class bindtag, or adding a new bindtag after the class bindtag and binding it to that.
Since binding to <<ListboxSelect>> is the better solution I won't go into details on how to modify the bindtags, though it's straight-forward and I think fairly well documented.