I have 2 Screens in my app (ScreenManager). One of them has ActionBar with ActionOverflow. I have button that should change current Screen and close ActionOverflow, but the Screen changes and ActionOverflow remains open until
I tap the screen in another place.
Here some code:
# .kv
ScreenManager:
id: ScrMan
Screen:
name: 'scr1'
BoxLayout:
orientation: 'vertical'
ActionBar:
ActionView:
ActionOverflow:
id: ActOv
#some buttons
ActionButton:
text: 'some text'
on_press:
ActOv.is_open = False
#I also tried: is_open = False; self.parent.is_open = False; with the same result
ScrMan.current = 'scr2'
Screen:
name: 'scr2'
#some cool stuff here
How I can change the screen and close ActionOverflow list?
Edit: As you have version 1.9.1 (or older master branch, there was this fix missing. Changing actionbar.py in <python dir>/Lib/site-packages/kivy/uix/actionbar.py brings the default behavior to the old version.
The dropdown should close on its own by default. Maybe there's something wrong in your code e.g. placing ActionButton into ActionOverflow as a widget or maybe something else. Try this:
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.uix.boxlayout import BoxLayout
Builder.load_string('''
#:import Clock kivy.clock.Clock
#:import partial functools.partial
<Test>:
ScreenManager:
id: ScrMan
Screen:
name: 'scr1'
ActionBar:
pos_hint: {'top': 1}
ActionView:
ActionPrevious:
ActionOverflow:
ActionButton:
text: 'some very very long text'
ActionButton:
text: 'some very very long text'
ActionButton:
text: 'some very very long text'
ActionButton:
text: 'some very very long text'
ActionButton:
text: 'some very very long text'
ActionButton:
text: 'try this button!'
on_press:
ScrMan.current = 'scr2'
Clock.schedule_once(partial(root.change_scr, ScrMan), 1)
Screen:
name: 'scr2'
Label:
text: 'second'
''')
class Test(BoxLayout):
def change_scr(self, man, *dt):
man.current = man.previous()
runTouchApp(Test())
Related
newbie here trying to test my hand on something new.I'm trying to display slider value from one screen on another screen. The screen I want the value to be displayed in has multiple screens embedded in it. the first picture is the first screen.
home screen that contains multiple screens
below is the code for this screen:
<Thermostat>:
BoxLayout:
orientation: 'horizontal'
cols: 2
Label:
id: Thmo
font_size: "11sp"
text: "INSIDE: 50"
Label:
text: "More Info"
font_size: "11sp"
<Weather>:
BoxLayout:
orientation: 'horizontal'
cols: 2
Label:
id:Temp
text: "OUT TEMP: "+ root.checkWeather()
font_size: "11sp"
Label:
id: Locale
text: root.locale
font_size: "11sp"
The next screen which is the command screen is here below, i want the slider value from this screen to be updated in the home screen. I have tried using a global variable called TEMP but it did not work. any help would be appreciated.
command screen
below is the code for the command screen:
<Temperature>:
BoxLayout:
cols: 3
Label:
text: 'THERMOSTAT'
Slider:
id: temp_slider
min: 40
max: 100
value: 40
step: 1
on_value: app.TEMP = str(temp_slider.value)
Label:
id: value
text: str(temp_slider.value)
<Vent>:
BoxLayout:
size_hint_y: None
height: '48dp'
cols: 3
Label:
text: 'VENT'
Button:
id: state
text: 'Open'
on_press: root.Open(state)
Label:
id: cmdErr
from kivy.app import App
value = App.get_running_app().root.ids.SCREENCONTROLLER.get_screen('SCREENAMEHERE').ids.CHILDWIDGETID.
Basically, you do your usual id traveling but you start from the very top of the running app and work downwards.
'root' here is the widget that is returned in the Apps Build Method. You haven't provided that code, so I'm assuming you returned the Screen Controller.
Once you get to the screen controller, you can use its get_screen method by entering the string name you gave to the screen you want to go to, then from their you find it through ids as if you were on that screen.
In a kivy app, I have some text-input widgets, I would like to label it as a ghost text. By ghost text, I mean it passes no value to the number to be called later on.
For example, say, I have two text input entries in a kivy app.
TextInput:
text: '1st number'
id: first_id
input_filter: 'float'
multiline: False
TextInput:
text: '2nd number'
id: second_id
input_filter: 'float'
multiline: False
When the app is run, the first text input has a default text "1st number" and the second text input has a default text "2nd number". I am linking them to a function that add the two floating numbers together via a method. The problem is if users didn't enter anything and press the "run" button, it will break the app. Is there a way to make the text non-passable to the function?
And also, I want it to be in the background, so that users don't have to tap the text input and then delete the '1st number' writing before entering a number, any way to do that?
I think I have created a small example the creates the behavior you wanted. I created a new widget with a TextInput and a Label and its only showing the label if the value is empty...
a = Builder.load_string("""
<FloatInput#FloatLayout>:
empty_text: "Input a number"
value: float(txt.text or '0.0')
TextInput:
id: txt
input_filter: 'float'
Label:
center: txt.center
size: self.parent.size or (300,300)
text: "" if self.parent.value else self.parent.empty_text
font_size: txt.font_size
color: 0,0,0,1
BoxLayout:
FloatInput:
id: fi
Label:
text: "%s" % fi.value
""")
class SimpleApp(App):
def build(self):
return a
SimpleApp().run()
You may use this class as your input widget...
I'm new to python and kivy so please be gentle :)
I have 3 tabs and want the app to show the next tab when I press button1 and button2, and to shutdown itself when button3 is pressed. I would like to use kivy builder to do this, if possible.
Please help :/
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.lang import Builder
class Root(TabbedPanel):
pass
Builder.load_string('''
<Root>
do_default_tab: False
size_hint: 1, 1
post_hint: {'center_x': .5, 'y': .5}
do_default_tab: False
TabbedPanelItem:
text: 'Step 1'
FloatLayout:
Button:
id: button1
size_hint: .10, .10
pos: 1350, 40
orientation: 'vertical'
text: 'Next!'
on_press: print("go to next step") #need help
TabbedPanelItem:
text: 'Step 2'
FloatLayout:
Button:
id: button2
size_hint: .10, .10
pos: 1350, 40
orientation: 'vertical'
text: 'Next!'
on_press: print("go to next step") #need help
TabbedPanelItem:
text: 'Step 3'
FloatLayout:
Button:
id: button3
size_hint: .10, .10
pos: 1350, 40
orientation: 'vertical'
text: 'The End.'
on_press: print("exiting") #need help
''')
class TabbedPanelApp(App):
def build(self):
return Root()
if __name__ == '__main__':
TabbedPanelApp().run()
TabbedPanel.switch_to seems pretty straightforward to me. Just use it like this in those three on_press:
root.switch_to(root.tab_list[1])
root.switch_to(root.tab_list[0])
root.switch_to(root.tab_list[2])
tab_list is in "reversed" order - the last added TabbedPanelItem is the first item in list.
I have looked everywhere in the ProMotion docs and examples but I am unable to find a way to change a TableScreen layout, specifically the TableView cell's vertical starting position.
I have an UIView on the top of my screen to show some buttons, and the TableView cells should start underneath, but right now they are on top of each other.
I have even managed to move the TableView using the REPL console:
rmq(4496872960).nudge d: 10
where 4496872960 is the id of my UITableViewWrapperView object, but I have no idea where to put the layout coordinates for this object in code.
My screen code:
class HomeScreen < PM::TableScreen
title I18n.t("home_screen.title")
tab_bar_item title: I18n.t("home_screen.title"), item: "icon-home_32x32.png"
row_height :auto, estimated: 30
stylesheet HomeScreenStylesheet
def on_load
#matches = [{attributes: {status: "dummy1", player2: {email: "dummy1#match.com"}}},{attributes: {status: "dummy2", player2: {email: "dummy2#match.com"}}}]
append(TopHomeView, :top_home_view)
set_nav_bar_button :left, title: I18n.t("home_screen.sign_out_label"), image: image.resource("icon-logout-32x32.png"), action: :sign_out
set_nav_bar_button :right, title: (Auth.current_user ? Auth.current_user["email"] : ""), image: image.resource("icon_user_50x50.png"), action: :open_profile
load_async
end
def table_data
[{
cells: #matches.map do |match|
{
title: match[:attributes][:player2][:email],
subtitle: match[:attributes][:status],
action: :play_round,
arguments: { match: match }
}
end
}]
end
EDIT:
I have kept trying to solve this and I have now added a style to my UITableViewWrapperView object like this:
def viewDidLoad
super
rmq(UITableViewWrapperView).apply_style(:style_for_table_wrapper)
end
In my stylesheet I am able to therefore style everything: background_color, hidden status, but the frame style is just ignored.
def top_home_view(st)
st.frame = {l:20, t: 20, w: 300, h: 60}
st.background_color = color.white
end
As indicated in this Github issue:
https://github.com/infinitered/ProMotion/issues/784#issuecomment-230962988
the solution here lies in implementing a Header view for the TableScreen. This lets us have an area on top of the cells for our own use:
Define a table_header_view returning an instance of a UIView (required):
class HomeScreen < PM::TableScreen
# ...
def table_header_view
create!(TopHomeView, :top_home_view)
end
Note that the "bang method" (create!) will return an instance of UIView.
Credit goes to https://github.com/andrewhavens for clearing this up
I woud like to create ScrollView with custom Buttons (Label + Image), but I've got problem with proper positioning Label and Image on Button object.
Code:
class TestScreen(Screen):
def change(self, btn, pos):
print pos
def populate(self):
scroll = self.ids.myscroll
grid = self.ids.scrollgrid
btn = Button(size_hint_y=None, width='29sp', text='')
box = BoxLayout(size=btn.size, pos=btn.pos,orientation='horizontal')
image = Image(source='image.png', size_hint_x=None, width=74)
label = Label(size_hint_x=None, width=100, text='test')
box.add_widget(image)
box.add_widget(label)
btn.add_widget(box)
grid.add_widget(btn)
btn.bind(pos=partial(self.change))
and .kv file:
<TestScreen>:
Button:
text: 'populate'
size_hint: None, None
size: 100,100
pos: 0,0
on_press: root.populate()
ScrollView:
id: myscroll
size_hint: None, None
size: 300, 500
pos: 100, 100
scroll_x: 0.5
GridLayout:
spacing: 20
padding: 20
id: scrollgrid
size_hint: None, None
cols: 1
size_hint_y: None
children
height: self.minimum_height
width: self.parent.width
The biggest problem is that during creating BoxLayout in populate(), btn.pos is [0,0] and after rendering btn.pos is changed to proper coordinates (I checked that using bind() for pos). How can I get correct cooridnates during creating mentioned BoxLayout?
If you add a widget to another widget, which isn't a layout, it will receive default pos (0,0) and size (100, 100). Use a grid layout to pack an image and a button together. Example:
main.py:
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen
from kivy.properties import StringProperty, ObjectProperty
from random import choice
class ImageButton(GridLayout):
images = [
'http://kivy.org/logos/kivy-logo-black-256.png',
'http://img.interia.pl/rozrywka/nimg/2/7/roz4286600.jpg',
'http://www.i-programmer.info/images/stories/News/2014/Apr/A/kivycont2.jpg',
'http://static.giantbomb.com/uploads/scale_small/9/90155/2472244-abathur.png',
'https://www.gravatar.com/avatar/a07de5a89d18964a22775deae84d9ba6?s=328&d=identicon&r=PG',
'https://www.gravatar.com/avatar/578e323e1c4dda99b24bf047e0cb2e3e?s=328&d=identicon&r=PG&f=1'
]
source = StringProperty(images[0])
def change_image(self):
while True:
new_img = choice(self.images)
if new_img != self.source:
self.source = new_img
break
class MainScreen(Screen):
grid = ObjectProperty()
def populate(self):
for i in xrange(10):
self.grid.add_widget(ImageButton())
class Test(App):
def on_start(self):
self.root.populate()
Test().run()
test.kv:
MainScreen:
grid: grid
ScrollView:
GridLayout:
id: grid
cols: 1
spacing: '10dp'
padding: '10dp'
size_hint_y: None
height: self.minimum_height
<ImageButton>:
cols: 2
size_hint_y: None
height: '200dp'
AsyncImage:
source: root.source
size_hint_x: 0.3
Button:
text: 'click me'
on_press: root.change_image()
Adding widgets to a Button isn't a very nice solution. Button actually is a Label with a behavior set to it, therefore you only duplicate it without any good reason. How to make such a widget(a button with an image) is explained here, you'd need only to choose a position for Image and position whole widget for your needs. In the first example the App class also behaves as a BoxLayout, so imagine it as a layout only - a root widget. From there it'll be easy.
Also, when a widget is initialized, it comes with the default values of size and pos and other values specific for the widget itself, which are then changed. Basically, you created a widget with btn = Button(), but didn't set any pos or size, so when you access the position later within box, you only take the defaults and well... place them to size and pos of another widget - so you only replaced defaults with the same values. To get correct values you'll need to set them first inside the widget to your desired pos&size.