In my kivy project, I have a button allowing me to generate a matplotlib graph in .png format. Generating this image takes time (around 20 seconds), and I would like to display a pop-up window to warn the user.
What i tried :
<MyPopup#Popup>:
auto_dismiss: False
Button:
text: 'This could take time, please wait :) '
on_release: root.dismiss()
and :
ActionButton:
text: 'generate graph'
on_release: Factory.MyPopup().open()
#on_release: root.generate_graph()
Unfortunately, if I uncomment the second "on_release", the pop_up window never appears?
Do you have any guess?
Thank you in advance!
To display a popup while a method is running I use threads. When the popup fires up, the method runs in a thread.
CODE:
def popupThread(self):
#set the popup structure
self.popup = ActivityBox(self)
self.popup.open()
# run the method in threads
t1 = threading.Thread(target = self.someMethod)
t1.start()
The popup is defined in the Builder.load_string():
def build(self):
sm = Builder.load_string("""
<ActivityBox>:
size_hint: 1, .7
auto_dismiss: False
title: 'some activity'
title_align: "center"
title_size: 30
BoxLayout:
orientation: "vertical"
Label:
font_size: '30sp'
text: 'work in progress'
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
""")
You were overwriting the on_release method.
ActionButton:
text: 'generate graph'
on_release:
Factory.MyPopup().open()
root.generate_graph()
Related
I have a dropdown menu in which I would like to know which value the user has selected so I can use it later in my program. This is my .kv code:
BoxLayout:
orientation: 'horizontal'
size_hint_x: 1
Button:
pos_hint:{'center_x': .5, 'center_y': .5}
id: units_num_btn
text: '0'
size_hint_y: None
height: 44
on_parent: drop_player_numbers_units.dismiss()
on_release: drop_player_numbers_units.open(self)
DropDown:
id: drop_player_numbers_units
on_select: units_num_btn.text = '{}'.format(args[1])
on_select: app.return_player_numbers()
Button:
id: units_num_btn_1
text: '1'
size_hint_y: None
height: 35
on_release: drop_player_numbers_units.select('1')
Button:
id: units_num_btn_2
text: '2'
size_hint_y: None
height: 35
on_release: drop_player_numbers_units.select('2')
and so on.
My .py code is here:
class drop_content(DropDown):
pass
class PlayerScreen(Screen):
pass
class TheApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(PlayerScreen(name='player_setup'))
sm.current = 'player_setup'
return sm
def main():
Builder.load_file('menu.kv')
app = TheApp()
app.run()
if __name__ == '__main__':
main()
I have previously used a function such as this:
# .py example
def return_text(self):
text = self.root.get_screen('screen').ids.specific_id.text
print(text)
# .kv example
TextInput:
id: input
text: "2"
on_text: app.return_text()
which did return text using a Textinput type in my .kv file. I know it doesn't work for the dropdown menu since the text is not inputted in the same way. Do you know how I would do this?
Thanks in advance
from kivy.app import App
from kivy.uix.dropdown import DropDown
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.lang.builder import Builder
class TestScreen(Screen):
def return_player_numbers(self,play_number):
print('Test : ',play_number)
kv = '''
ScreenManager:
TestScreen:
<TestScreen>:
BoxLayout:
orientation: 'horizontal'
size_hint_x: 1
Button:
pos_hint:{'center_x': .5, 'center_y': .5}
id: units_num_btn
text: '0'
size_hint_y: None
height: 44
on_parent: drop_player_numbers_units.dismiss()
on_release: drop_player_numbers_units.open(self)
DropDown:
id: drop_player_numbers_units
on_select:
units_num_btn.text = '{}'.format(args[1])
app.root.current_screen.return_player_numbers(args[1])
app.return_player_numbers(args[1])
Button:
id: units_num_btn_1
text: '1'
size_hint_y: None
height: 35
on_release: drop_player_numbers_units.select('1')
Button:
id: units_num_btn_2
text: '2'
size_hint_y: None
height: 35
on_release: drop_player_numbers_units.select('2')
'''
class TheApp(App):
def return_player_numbers(self,play_number):
print('App : ',play_number)
def build(self):
return Builder.load_string(kv)
if __name__ == '__main__':
TheApp().run()
I have found a way that I think is simpler than the current answer provided. This method uses that fact that the text on the button used to initiate the drop down changes as another button is selected. This is to show the user what they have selected. Since I want to know the text the user has selected from the drop down menu, and this is in fact the same thing that is updated on the initial button, I can just read the text from the button to obtatin my result like so:
(in .kv file)
<player_setup>
BoxLayout:
orientation: 'horizontal'
size_hint_x: 0.2
pos_hint: {'x':.4, 'y':0}
Button:
pos_hint:{'center_x': .5, 'center_y': .5}
id: tens_num_btn
text: '0'
size_hint_y: None
# size_hint_x: 0.1
height: 44
on_parent: drop_player_numbers_tens.dismiss()
on_release: drop_player_numbers_tens.open(self)
DropDown:
id: drop_player_numbers_tens
on_select:
#######
tens_num_btn.text = '{}'.format(args[1])
# this line here ^ is the one that re-formats the text of the button above to
# the selected text
app.return_player_numbers()
max_height: 120
(.py file)
def return_player_numbers(self):
player_number = self.root.get_screen('player_setup').ids.tens_num_btn.text
return player_number
This also allows me to concatenate multiple dropdown results using a single function, however it is menu specific. In my case, this works better for me
I'm trying to make that if i click tab button the content of this tab will change. In this specific example, I want to change TextInput to day that it is today. But I have no clue how to execute function from WoPlan class in Menu class. Or just how to influence the WoPlan textinput via menu's button from tab.
.kv
<Menu>:
TabbedPanel:
do_default_tab: False
ORM:
font_size: 30
text: "ORM"
WoPlan:
font_size:30
text: "WOP"
on_press: root.WoPlan().weekday()
<WoPlan>:
BoxLayout:
orientation: "vertical"
TextInput:
id: weekday
disabled: True
.py
class WoPlan(TabbedPanelItem):
def weekday(self):
curr_date = date.today()
weekday = calendar.day_name[curr_date.weekday()]
self.ids.weekday.text = weekday
class Menu(Screen):
pass
Instead of root.WoPlan().weekday(), you can write self.weekday().
The "self" refers to the Widget, in which the on_press is called ("WoPlan" in this case).
If you want to call the function from any other widget within the "Menu" Screen, e.g.:
<Menu>:
TabbedPanel:
do_default_tab: False
ORM:
font_size: 30
text: "ORM"
Button: #example widget
on_release: root.ids["woplan"].weekday()
WoPlan:
id: woplan
font_size:30
text: "WOP"
I found out I can do that with current_tab: self.weekday()
I just needed to put it in here
<Menu>:
TabbedPanel:
do_default_tab: False
ORM:
font_size: 30
text: "ORM"
WoPlan:
font_size: 30
text: "WOP"
current_tab: self.weekday()
My GUI consists of a Title bar with buttons who control a screenmanager in the middle, then the actual screen manager, and a bottom bar with status updates.
Usually i controll the screens in the middle with the buttons on top. Easy. Now my problem is, that one of those screens is super complex already, and i have a problem that i have so many indentations.. So, here we go:
I want to have a screen manager sm2 inside another screenmanager sm1, and access screens from sm1 from within sm2.
I tried to recreate my problem with the following syntax, i hope i did not forget anything important. I tried playing around with dynamic classes and such, but my code is linked to a lot of python code which i access with root.functions and it didnt quite work.
Kivy syntax:
BoxLayout:
size_hint: 1, 0.1
Label:
text: 'Title Bar with Menu'
Button:
text: 'Go to Screen 1'
on_release: sm1.current = 'outerScreen1'
Button:
text: 'Go to Screen 2'
on_release: sm2.current = 'outerScreen2'
Screenmanager:
id: sm1
Screen:
name: 'outerScreen1'
BoxLayout:
Label:
text: 'Testlabel'
Screen:
name: 'outerScreen2'
BoxLayout:
orientation: 'horizontal'
size_hint: 1, 0.1
anchor_y: 'top'
pos_hint: {'top': 1}
Label:
text: 'Title Bar inside outerScreen2'
Button:
text: 'Go to innerscreen Home'
on_release: sm2.current = 'screen2Home'
Button:
text: 'Go to innerscreen Meow'
on_release: sm2.current = 'screen2Meow'
Button:
text: 'Go to outerscreen 1, but inside screenmanager sm2'
on_release: sm2.current = 'outerScreen1' **#This is where the problem is**
BoxLayout:
orientation: 'horizontal'
size_hint: 1, 0.9
ScreenManager:
id: sm2
Screen:
name: 'screen2Home'
Label:
text: 'simple test'
Screen:
name: 'screen2Meow'
Label:
text: 'I meow sometimes'
You can do what you want, but it requires an ugly hack. In order to set the current Screen of a ScreenManager, that Screen must be a child of the ScreenManager. But any Widget (including Screens) can only have one parent. So, the ugly hack is to move the Screen between ScreenManagers as needed.
Part of a modified version of your kv implements part of the hack:
ScreenManager:
id: sm1
Screen:
id: outer_screen_1 # added id
name: 'outerScreen1'
BoxLayout:
Label:
text: 'Testlabel'
Screen:
name: 'outerScreen2'
BoxLayout:
orientation: 'horizontal'
size_hint: 1, 0.1
anchor_y: 'top'
pos_hint: {'top': 1}
Label:
text: 'Title Bar inside outerScreen2'
Button:
text: 'Go to innerscreen Home'
on_release: sm2.current = 'screen2Home'
Button:
text: 'Go to innerscreen Meow'
on_release: sm2.current = 'screen2Meow'
Button:
text: 'Go to outerscreen 1, but inside screenmanager sm2'
on_release:
# use saved id to move Screen between ScreenManagers
sm1.remove_widget(outer_screen_1)
sm2.add_widget(outer_screen_1)
# now we can use this Screen with sm2
sm2.current = 'outerScreen1' #This is where the problem is
I have added an id to the outerScreen1. And in the problem area, the on_release code now moves outerScreen1 from sm1 to sm2 and make it the current screen of sm2.
But now the code using sm1:
Button:
text: 'Go to Screen 1'
on_release: sm1.current = 'outerScreen1'
will fail because outerScreen1 is no longer a child of sm1. So, if you want to go this route, you must keep track of where outerScreen1 is, and change its parent as needed.
This is a good time to consider whether just making another Screen that duplicates outerScreen1 might be simpler.
I am trying create a GUI by implementing the template of the ComicCreator GUI sample as a template for my own project. The code is easy to follow, but I would like to be able to reconfigure the drawingspace.kv, each time a button is pushed, say for example something like this:
Q: How could I configure the drawingspace.kv to have a different layout with different widgets for each button that is pressed?
A neat way to do this is to use screen.
Since I allready have an example of this app from you earlier question, it was easy to implement the screens, and rewrite the classes a bit.
When a button is pressed, you set the screenmanager's current to whatever the name you named the screen you want.
Then you just edit the layouts as you want inside of each screen, in the kv file, or python file.
I choose to make most of the layout stuff in kv language here. Because I find it easier to develop a layout the way I want it this way.
Later I could rewrite it to python if I want that.
So my python file looks like this now:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.clock import Clock
from kivy.uix.screenmanager import Screen,ScreenManager,NoTransition
from kivy.lang import Builder
import time
Builder.load_file("kv.kv")
class MyLayout(BoxLayout):
def __init__(self,**kwargs):
super(MyLayout,self).__init__(**kwargs)
self.orientation = "vertical"
self.padding = 10
class MainScreen(Screen):
pass
class RemoveScreen(Screen):
pass
class GroupScreen(Screen):
pass
class MyLogo(BoxLayout):
your_time = StringProperty()
def __init__(self,**kwargs):
super(MyLogo,self).__init__(**kwargs)
Clock.schedule_interval(self.set_time, 0.1)
def set_time(self,dt):
self.your_time = time.strftime("%m/%d/%Y %H:%M")
class MyApp(App):
def __init__(self,**kwargs):
super(MyApp,self).__init__(**kwargs)
self.sm = ScreenManager(transition=NoTransition())
self.sm.add_widget(MainScreen(name = "main"))
self.sm.add_widget(RemoveScreen(name = "remove"))
self.sm.add_widget(GroupScreen(name = "group"))
self.sm.current = "main"
def build(self):
return self.sm
if __name__ == "__main__":
MyApp().run()
And kv.kv file looks like this:
#:kivy 1.9.1
<MyButtons#BoxLayout>:
padding: 10,10,10,0
spacing: 10
size_hint: 1,0.3
orientation: "horizontal"
Button:
text: "Clear"
on_press: app.sm.current = "main"
Button:
text: "Remove"
on_press: app.sm.current = "remove"
Button:
text: "Group"
on_press: app.sm.current = "group"
Button:
text: "Color"
Button:
text: "Gestures"
<MyLogo>:
spacing: 10
padding: 10,10,10,0
orientation: "horizontal"
BoxLayout:
orientation: "vertical"
size_hint: 0.3,1
canvas:
Rectangle:
pos: self.pos
size: self.size
AsyncImage
source: 'http://lmsotfy.com/so.png'
Label:
size_hint: 1,0.3
text: root.your_time
color: [0,0,0,1]
Label:
size_hint: 1,0.3
text: "NYC, New York, USA"
color: [0,0,0,1]
<MainScreen>:
MyLayout:
MyLogo:
#Button:
# text: "main"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
<RemoveScreen>:
MyLayout:
MyLogo:
BoxLayout:
orientation: "horizontal"
Label:
font_size: "40sp"
text: "Remove"
Button:
font_size: "20sp"
text: "Remove this or something"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
<GroupScreen>:
MyLayout:
MyLogo:
BoxLayout:
orientation: "vertical"
Label:
font_size: "40sp"
text: "Group"
Button:
font_size: "20sp"
text: "Something groups stuff"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
The layout frame should be a screen manager, and each layout a screen. Screen transitions would be then triggered by pressing the buttons. You can also watch a tutorial here if you don't know how to do this, but the documentation should be enough.
I am having difficulty figuring out how to correctly change screens using the on_press attribute of a button inside of a ModalView widget.
On pressing the button in the ModalView, I want the screen to change to the game_screen_name defined in the Game1HomeScreen class and other GameHomeScreen classes (as is done with the NewGameButton and SavedGameButton below). This app has multiple games, so I would rather not make a call directly to Game1HomeScreen1().game_screen_name and want to instead keep it generic, so game_screen_name takes on the value of the class from which NewGamePopup is called.
What is a good way to do this?
The main.py code:
class Game1HomeScreen(Screen):
game_screen_name = 'game1_gameboard_screen_name'
class NewGamePopup(ModalView):
pass
class GamesApp(App):
sm = ScreenManager()
def show_new_game_popup(self):
p = NewGamePopup()
p.open()
def prev_screen(self):
self.sm.current = self.game_screen_name #this line does not work of course, because there is no game_screen_name variable in the NewGamePopup class.
The .kv code:
<NewGamePopup>:
size_hint: .5, .3
NewGameBoxLayout:
padding: [10,10,10,10]
orientation: 'vertical'
Label:
font_name: 'fonts/playce.ttf'
font_size: '14sp'
markup: True
text: '[color=#000000]Are you sure? Current game will be erased![/color]'
Button:
font_name: 'fonts/playce.ttf'
font_size: '14sp'
text: 'Confirm'
background_normal: 'img/red_button5.png'
background_down: 'img/red_button5.png'
size_hint_y: None
on_press: root.dismiss(); app.prev_screen()
<Game1HomeScreen>:
GeneralBoxLayout:
BannerGridLayout1:
BodyBoxLayout:
rows: 2
Image:
source: 'img/logo.png'
size_hint: (1.0,.9)
GridLayout:
cols: 2
spacing: '5dp'
padding: '5dp'
size_hint: (1.0,.1)
NewGameButton:
id: game1
on_press:
if saved_game1.disabled == False: app.show_new_game_popup()
else: root.manager.current = root.game_screen_name; saved_game1.disabled = False
SavedGameButton:
id: saved_game1
on_press: root.manager.current = root.game_screen_name;
FooterGridLayout:
ReturnButton:
text: 'Return to main menu'
on_press: root.manager.current = 'home'
Save the game screen name in a string property when the game is selected
from kivy.properties import StringProperty
....
class GamesApp(App):
game_screen_name = StringProperty('')
Then you can use the sm.current call later as needed. Too many things were left out of the code snippet in the question to create a working version; even the build method was missing.