how to run the same task multiple times concurrently on kivy? - python

for example, with this code i print with a while true loop the text that is in the inputs, after that is running i want to do it the same but with different text but when i trigger the task it starts printing the new text and the first one no. i tried already with threading and multithreading library but the result is the same
.kv file
screen_helper = '''
ScreenManager:
Loginscreen:
<Loginscreen>:
name: 'login'
MDTextField:
id: emaillogin
hint_text: "E-mail"
pos_hint: {"center_x": 0.5, "center_y": 0.70}
size_hint_x: None
width: 300
MDTextField:
id: passwordlogin
hint_text: "Password"
pos_hint: {"center_x": 0.5, "center_y": 0.60}
size_hint_x: None
width: 300
MDLabel:
text: "Welcome to Profit Alert"
pos_hint: {"center_x": 0.7, "center_y": 0.80}
size_hint_x: None
width: 500
MDRaisedButton:
text: "Sign up"
pos_hint: {"center_x": 0.5, "center_y": 0.45}
on_press: root.calling()
MDTextButton:
text: "Don't have an account?, create a new one"
pos_hint: {"center_x": .5, "center_y": .25}
on_press: root.manager.current = 'request'
MDTextButton:
text: "Forgot password?"
pos_hint: {"center_x": .5, "center_y": 0.35}
on_press: root.call()
.py file
class Loginscreen(Screen):
def __init__(self, **kw):
super().__init__(**kw)
def calling(self, *args):
executor = ThreadPoolExecutor(max_workers=5)
executor.submit(self.func)
def imprimir(self, *args):
self.palabra1 = self.ids.emaillogin.text
self.palabra2 = self.ids.passwordlogin.text
def func(self, *args):
Clock.schedule_once(self.imprimir, 0)
while True:
time.sleep(1)
print(self.palabra1, self.palabra2)
def call(self, *args):
Clock.schedule_interval(self.imprimir, 1 / 1)
pass
class work(MDApp):
def build(self):
self.theme_cls.primary_palette = "Yellow"
screen = Builder.load_string(screen_helper)
return screen
work().run()
i tried with threading and multiprocessing pool

Let me put it this way a kivy gui it self is a loop u cant put an infinite loop inside the gui cause you will push it to a bug. the only way is use a thread. follow this example and u ll find ur problem.
Lets say we have a main.py file like this:
import threading
import # what u need
Builder.load_file('the.kv')
some_cond = False # Note that this function is gonna be
string = '' # running on thread and threads can't
some_function(): # be started twice or stopped
global string, some_cond # and restarted
while True: # the while True is meant to keep
if some_cond == True: # the thread alive
print(string) #
some_thread = threading.Thread(target = some_function)
class fscreen(Widget):
def __init__(self, **kwargs):
global string, some_cond ## dont forget this
super().__init__(**kwargs)
Clock.schedule_interval(update_string, 1) # 1 time per sec
# this Clock is meant to update the string the outer thread
def update_string(self, *args):
global string
string = self.ids.my_text_input.text
#### assuming that some_cond = True the thread is gonna
### print(string) endlessly for that u may consider managing
## some_cond True or False with a Button or something
# depends on when and what u want to print
class secscreen(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
pass
class thscreen(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
pass
class theapp(App):
def build(self):
self.screenm = ScreenManager() #(transition=FadeTransition())
self.fscreen = fscreen()
screen = Screen(name = "first screen")
screen.add_widget(self.fscreen)
self.screenm.add_widget(screen)
self.secscreen = secscreen()
screen = Screen(name = "secondscreen")
screen.add_widget(self.secscreen)
self.screenm.add_widget(screen)
self.thscreen = thscreen()
screen = Screen(name = "thirdscreen")
screen.add_widget(self.thscreen)
self.screenm.add_widget(screen)
return self.screenm
if __name__ == "__main__":
theapp = theapp()
some_thread.start() # this way both the gui and
threading.Thread(target = theapp.run()) # and the function are threading
Now in ur the.kv file i have something like this:
<fscreen>
TextInput:
id: my_text_input
size: ## whatever u want
pos: ## whatever u want

Related

KivyMD MDTimePicker is not returning time, MDTimePicker returns None

I'm trying to make a program where the program makes the user pick a due date and time for their tasks. User presses a button to open a dialog that takes task name, description and due date-time from them. Everything is fine, except I couldn't make the get_time() function do anything other than return time. And it returns None instead of the picked time. I made a seperate test app just to test if the picker works and it does. It can change the text of a label too, not just return time.
main.py
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.dialog import MDDialog
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.picker import MDDatePicker, MDTimePicker
from datetime import datetime
from kivymd.uix.list import TwoLineListItem
class MainApp(MDApp):
task_list_dialog = None
def build(self):
return Builder.load_file('main.kv')
def show_task_dialog(self):
if not self.task_list_dialog:
self.task_list_dialog = MDDialog(
title="Create Task",
type="custom",
content_cls=DialogContent(),
)
self.task_list_dialog.open()
def close_dialog(self, *args):
self.task_list_dialog.dismiss()
def add_task(self, task, description, task_date):
self.root.get_screen("testwin").ids['container'].add_widget(ListItem(text="[size=18]"+task.text+"[/size]", secondary_text=task_date))
print(task.text+"\n", description.text+"\n", task_date)
task.text = '' # set the dialog entry to an empty string(clear the text entry)
description.text = ''
class DialogContent(MDBoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.ids.date_text.text = str(datetime.now().strftime('%A %d %B %Y, %H:%M'))
def show_date_picker(self):
date_dialog = MDDatePicker()
date_dialog.bind(on_save=self.on_save)
date_dialog.open()
def on_save(self, instance, value, date_range):
date = value.strftime('%A %d %B %Y')
time = self.show_time_picker()
self.ids.date_text.text = date+str(time)
def show_time_picker(self):
time_dialog = MDTimePicker()
time_dialog.bind(time=self.get_time)
time_dialog.open()
def get_time(self, instance, time):
return time
class ListItem(TwoLineListItem):
def __init__(self, pk=None, **kwargs):
super().__init__(**kwargs)
self.pk = pk
class WindowManager(ScreenManager):
pass
class TestWindow(Screen):
pass
if __name__ == "__main__":
MainApp().run()
main.kv
#: include testwindow.kv
WindowManager:
TestWindow:
testwindow.kv
<TestWindow>:
name: "testwin"
Screen:
BoxLayout:
orientation: "vertical"
BoxLayout:
orientation: 'vertical'
ScrollView:
do_scroll_x: False
do_scroll_y: True
pos_hint: {"center_x": 0.5, "center_y": 0.5}
size_hint: 1, 0.7
MDList:
id: container
MDFloatingActionButton:
icon: 'plus-thick'
on_release: app.show_task_dialog()
elevation_normal: 10
pos_hint: {'x': 0.82, 'y': 0.07}
<DialogContent>:
orientation: "vertical"
spacing: "10dp"
size_hint: 1, None
height: "420dp"
BoxLayout:
orientation: 'vertical'
MDTextField:
id: task_text
hint_text: "Task"
on_text_validate: (app.add_task(task_text, task_desc, date_text.text), app.close_dialog())
MDTextField:
id: task_desc
mode: "fill"
multiline: True
hint_text: "Task Description"
size_hint_y: 0.1
BoxLayout:
orientation: 'horizontal'
size_hint_y: 0.1
MDIconButton:
icon: 'calendar'
on_release: root.show_date_picker()
padding: '10dp'
MDLabel:
spacing: '10dp'
font_size: 15
id: date_text
BoxLayout:
orientation: 'horizontal'
size_hint_y: 0.1
MDRaisedButton:
id: savetask
text: "SAVE"
on_release: (app.add_task(task_text, task_desc, date_text.text), app.close_dialog())
MDFlatButton:
id: canceltask
text: 'CANCEL'
on_release: app.close_dialog()
<ListItem>:
id: the_list_item
markup: True
multiline: True
The problem is that in method on_save you assigned the returned value of the method show_time_picker (which is clearly None) to a var. time. That's why you never get the time value (instead of that None).
One of many ways to achieve that is as follows:
First create class attributes to store the desired values. This way you will be able to separate attrs. from methods and access those attrs. anywhere in your code.
class DialogContent(MDBoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Create new attributes to save desired data later.
self._time = None
self._data = None
self.ids.date_text.text = str(datetime.now().strftime('%A %d %B %Y, %H:%M'))
def on_save(self, instance, value, date_range):
# Assign the date value hare.
self._date = value.strftime('%A %d %B %Y')
# Open time picker.
self.show_time_picker()
def show_time_picker(self):
time_dialog = MDTimePicker()
# time_dialog.bind(time=self.get_time)
time_dialog.bind(on_save=self.get_time)
time_dialog.open()
# Here's a conventional trick for checking what 'args' or 'kwargs' are used by some method.
# For that, define that function with variable no. of args and kwargs and print those.
# def get_time(self, *args, **kwargs):
# print(args, kwargs)
# As you see that should be two args, 'MDTimePicker' object and the 'time' value but with no kwargs.
# So you can write it as,
def get_time(self, instance, time):
# Assign the time value here.
self._time = time
# Now set date and time.
self.ids.date_text.text = self._date+str(self._time)
Note:
Since in method show_time_picker you bind a kivy property time (of MDTimePicker) to a method (get_time), so this method (get_time) will only be called whenever the value (time) changes. In simple words, say, the user just opens and then closes the time-picker without modifying the time, then as the value has not changed, the default value (None) will be used.
That's why it is safe to use (by forcing the user to use ok button) another default method on_save.

Kivy - Why doesn't image source update?

I want to update the source of an image when root.stop_recording() is called in the kv file, but the command self.ids.Mic_Image.source = self.Mic_Loading doesn't update the image. Instead the previous image (a gif opened in a zip file) just freezes. How can I fix this?
Here's the relevant section of my Python code:
class IdentifyItem(Screen):
Mic_Loading = StringProperty()
Mic_Listening = StringProperty()
Mic_Pressed = StringProperty()
Mic_Static = StringProperty()
def __init__(self, **kwargs):
super(IdentifyItem, self).__init__()
self.Mic_Loading = 'Mic_Loading.zip'
self.Mic_Listening = "Mic_Listening.zip"
self.audio_to_text = AudioToText()
def on_press(self):
self.ids.Mic_Image.anim_loop = 0
self.ids.Mic_Image.source = self.Mic_Listening
self.record = self.audio_to_text.start()
self.audio_thread = threading.Thread(target=self.record, args=(),
daemon=True) # initialises an instance of Thread.
self.audio_thread.start() # starts the Thread instance
def stop_recording(self):
self.ids.Mic_Image.source = self.Mic_Loading
self.audio_to_text.stop()
And the .kv file:
Image:
id: Mic_Image
source: "Mic_Static.png"
opacity: 1
anim_delay: 0
size_hint: (0.9,0.9)
pos_hint: {"center_x":0.5, "center_y": 0.2}
Button:
id: mic
background_color: (1, 1, 1, 0)
pos_hint: {"center_x":0.5, "y":0.08}
size_hint: (0.26,0.19)
on_press:
root.on_press()
on_release:
root.stop_recording()

Enabling/disabling buttons onclick through kv file

I wrote a simple code to create a timer, and for this I added two buttons: Start and Stop. The idea is that when one of the buttons is enabled the other isn't, and viceversa.
The code below works, but I want to get the same behaviour using the kv file.
def update_time(self, instance):
timer = Clock.schedule_interval(self.timer, 1.0 / 1.0)
instance.disabled = True
self.stop_button.disabled = False
def stop_updating(self, instance):
Clock.unschedule(self.timer)
instance.disabled = True
self.timer_button.disabled = False
If I try to use the code snippet below, the script throws an error because it is lacking one argument (instance).
Button:
text: 'Start'
on_press: root.update_time()
I know the "disabled" property in kv file exists, but I don't know how (or whether) to use it to modify the two buttons I want to be modified, onclick.
Any idea how to go about it?
All script code
class LandingScreen(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def go_to_timer(self):
clock_app.screen_manager.current = 'Timer'
class TimerScreen(GridLayout):
current_time = StringProperty()
timer_start = ObjectProperty(Button)
timer_stop = ObjectProperty(Button)
tot_sec = 0
def __init__(self, **kwargs): # Widgets without .kv file
super().__init__(**kwargs)
self.rows = 4
self.current_time = '00:00'
def update_time(self, instance):
timer = Clock.schedule_interval(self.timer, 1.0 / 1.0)
instance.disabled = True
# self.stop_button.disabled = False
self.timer_stop.disabled = False
def stop_updating(self, instance):
Clock.unschedule(self.timer)
instance.disabled = True
# self.timer_button.disabled = False
self.timer_start.disabled = False
def timer(self, instance):
# Code
class ClockApp(App):
def build(self):
self.screen_manager = ScreenManager()
# Landing screen
self.landing_screen = LandingScreen()
screen = Screen(name='Landing')
screen.add_widget(self.landing_screen)
self.screen_manager.add_widget(screen)
# Timer screen
self.timer_screen = TimerScreen()
screen = Screen(name='Timer')
screen.add_widget(self.timer_screen)
self.screen_manager.add_widget(screen)
return self.screen_manager
def go_to_landing(self):
clock_app.screen_manager.current = 'Landing'
if __name__ == '__main__':
clock_app = ClockApp()
clock_app.run()
kv file
<TimerScreen>:
rows: 5
Label:
font_size: 60
text: root.current_time
GridLayout:
cols: 3
Label:
text: ''
Button:
id: 'timer_start'
text: 'Start'
on_press: root.update_time(self)
Label:
text: ''
Label:
text: ''
Button:
id: 'timer_stop'
text: 'Stop'
on_press: root.stop_updating(self)
disabled: True
Label:
text: ''
Label:
text: ''
Label:
text: ''
Label:
text: ''
AnchorLayout:
anchor_x: 'left'
Button:
text: 'Back'
width: root.width / 7
size_hint_x: None
on_press: app.go_to_landing()
<LandingScreen>:
rows: 7
Label:
font_size: 50
text: 'Clock types'
canvas.before:
Color:
rgba: 1, 0, 0, 1
GridLayout:
cols:3
Label:
text: ''
Button:
text: 'Timer'
on_press: root.go_to_timer()
Label:
text: ''
Label:
text: ''
Button:
text: 'Countdown'
on_press: root.go_to_countdown()
Label:
text: ''
Label:
text: ''
Label:
text: ''
I think your code is mostly working. Just a few notes on the use of the ids:
You should not use a string literal as an id. Instead of id: 'timer_start', use id: timer_start. If you use a string literal, you will encounter problems if you try to reference it elsewhere in the kv (it will be interpreted as a string, not an id).
When declaring an ObjectProperty in a class that will be defined by an id in the kv, use ObjectProperty(None) in the declaration in the class.
Use declarations in the kv to match up with the declarations in the class.
Applying the above to your python code:
class TimerScreen(GridLayout):
current_time = StringProperty()
timer_start = ObjectProperty(None)
timer_stop = ObjectProperty(None)
tot_sec = 0
and in the corresponding kv:
<TimerScreen>:
timer_start: timer_start
timer_stop: timer_stop
rows: 5
and the ids should be changed to:
Button:
id: timer_start
text: 'Start'
on_press: root.update_time(self)
and:
Button:
id: timer_stop
text: 'Stop'
on_press: root.stop_updating(self)
disabled: True
The typical declaration of an ObjectProperty in kv like:
timer_start: timer_start
can be confusing, but that is how it is usually done. I prefer to use a different name for the ObjectProperty, to make it clearer. The first name in the above is the name of the ObjectProperty, and the second is the id. They just happen to have the same name.

Kivy label widget issue

ScrollView:
id: historyscroll
size_hint: 1, 0.925
pos_hint: {"x": 0, "top": 1}
Label:
id: historybox
text: "start"
size_hint: 1, None
size_hint_y: None
height: self.texture_size[1]
Issue:
Text is not displayed. Adding new text to the label causes the word pause to get displayed with different font sizes.
Issue2:
TextInput:
text: "localhost:"
size_hint: 1, 0.06
pos_hint: {"x": 0, "y": 0}
id: cmdbox
multiline: False
text_validate_unfocus:False
font_size: "20sp"
Upon typing something in the text box, sometimes some wierd image gets displayed. Also text_validate_unfocus: False does not prevent text box from unfocusing when enter key is pressed
Edit:
Whole code:
main.py:
#-*-coding:utf8;-*-
#qpy:3
#qpy:kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.clock import Clock
from kivy.core.window import Window
from game import Game
class Sm(ScreenManager):
pass
class GameScreen(Screen):
def __init__(self, **kwargs):
super(GameScreen, self).__init__(**kwargs)
self.game = Game()
self.add_widget(self.game)
self.ids.cmdbox.bind(on_text_validate=self.cmdreturn)
self.ids.cmdbox.bind(focus=self.cmdfocus)
self.ids.historybox.text=" "*(Window.width*75/1048)
#Clock.schedule_interval(self.render, 0.5)
def cmdreturn(self, args):
self.cmd = self.ids.cmdbox.text
#self.ids.historybox.insert_text("\n"+self.cmd)
self.ids.historybox.text += "\n" + self.cmd
self.cmdexecute(self.cmd.split(str(self.game.current_)+":")[1])
self.ids.cmdbox.text = str(self.game.current_) + ":"
def cmdexecute(self, cmd):
print(cmd)
if cmd == "fill":
self.ids.historybox.text+="\nfill"*30
if cmd == "clear":
self.ids.historybox.text= " "*(Window.width*75/1048)
if cmd == "ls":
self.ids.historybox.text= "\n"+str(self.game.current.folders.keys())
def cmdfocus(self, instance, value):
if value:
self.ids.cmdbox.pos_hint={"x":0, "y":0.45}
self.ids.historyscroll.size_hint=(1, 0.475)
else:
self.ids.cmdbox.pos_hint={"x":0, "y":0}
self.ids.historyscroll.size_hint=(1, 0.925)
class MenuScreen(Screen):
pass
class PauseScreen(Screen):
pass
class OptionsScreen(Screen):
pass
class GameApp(App):
def build(self):
sm = Sm()
sm.add_widget(MenuScreen(name="menu"))
sm.add_widget(GameScreen(name="game"))
sm.add_widget(PauseScreen(name="pause"))
sm.add_widget(OptionsScreen(name="options"))
sm.transition = NoTransition()
return sm
GameApp().run()
game.kv:
#kivy 1.10.0
<GameScreen>:
id:gscreen
FloatLayout:
Button:
text:"pause"
size_hint:(0.15, 0.05)
pos_hint:{"right":0.98, "top":0.98}
on_press:root.manager.current="pause"
ScrollView:
id: historyscroll
size_hint: 1, 0.925
pos_hint: {"x": 0, "top": 1}
Label:
id: historybox
#readonly: True
text: "start"
size_hint: 1, None
size_hint_y: None
height: self.texture_size[1]
#height: max(self.minimum_height, historyscroll.height)
#multiline: True
#foreground_color: (1,1,1,1)
#background_color: (255,255,255,0)
#font_size: "17sp"
#halign: "left"
#text_size:(self.width, "None")
TextInput:
text: "localhost:"
size_hint: 1, 0.06
pos_hint: {"x": 0, "y": 0}
id: cmdbox
multiline: False
text_validate_unfocus:False
font_size: "20sp"
<MenuScreen>:
FloatLayout:
Button:
text:"start"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.61}
on_press:root.manager.current="game"
Button:
text:"options"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.5}
on_press:root.manager.current="options"
Button:
text:"exit"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.39}
on_press:quit
<OptionsScreen>:
FloatLayout:
Button:
text:"back"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.5}
on_press:root.manager.current="menu"
<PauseScreen>:
FloatLayout:
Button:
text:"back"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.495}
on_press:root.manager.current="game"
Button:
text:"exit"
size_hint:(0.3, 0.1)
pos_hint:{"center_x":0.5, "center_y":0.605}
on_press:root.manager.current="menu"
game.py:
from kivy.uix.widget import Widget
from random import randint
class Game(Widget):
def __init__(self, **kwargs):
super(Game, self).__init__(**kwargs)
self.localhost = "5255.7611"
self.internet = Internet(self.localhost)
self.current_ = "localhost"
self.current = self.internet.links[self.localhost.split(".")[0]].links[self.localhost.split(".")[1]]
class Internet:
def __init__(self, localhost):
self.links = {}
self.links[str(localhost)[:4]] = Router(str(localhost)[:4], self)
self.links[str(localhost)[:4]].islocal(localhost)
def Crouter(self):
tmp = str(randint(1000, 9999))
if not str(tmp) in self.links:
self.links[str(tmp)] = Router(tmp, self)
else: self.Crouter
class Computer:
def __init__(self, ip, router):
self.ip = ip
self.router = router
self.islocal = False
self.folders = {"programs":Folder("programs",[], {}),
"downloads":Folder("downloads",[], {})}
class Folder:
def __init__(self, name, content, data):
self.content = content
self.data = data
class File:
def __init__(self, content, data):
self.content = content
self.data = data
class Router:
def __init__(self, ip, internet):
self.ip = ip
self.internet = internet
self.links = {}
def Ccomputer(self):
tmp = str(randint(1000, 9999))
if not str(tmp) in self.links:
self.links[str(tmp)] = Computer(str(tmp)+self.ip, self)
else: self.Ccomputer
def islocal(self, localhost):
self.links[str(localhost)[5:]] = Computer(str(localhost), self)
self.links[str(localhost)[5:]].islocal = True
(Btw, I am using qpython-kivy)
Issue in brief:
The text input(id:cmdbox) on editing sometimes displays wierd images in place of text.
Also, the label(id:historybox) does not display the correct text(it only displays "pause" in different font size each time)
Edit2:
Finally got some images.
https://www.dropbox.com/sh/i6t192ujys2hivz/AACPR5Sgb72Mv8M7gB3DiGmNa?dl=0
For the first issue, in the __init__ of your screen you are replacing the text of your label
For the second, I don't know if that property exists in kivy If you want to keep the focus in your TextInput Try this:
...
class GameScreen(Screen):
def __init__(self, **kwargs):
super(GameScreen, self).__init__(**kwargs)
self.game = Game()
self.add_widget(self.game)
self.ids.cmdbox.bind(on_text_validate=self.cmdreturn)
self.ids.cmdbox.bind(focus=self.cmdfocus)
#self.ids.historybox.text=" "*(Window.width*75/1048)
#Clock.schedule_interval(self.render, 0.5)
def cmdreturn(self, args):
self.cmd = self.ids.cmdbox.text
#self.ids.historybox.insert_text("\n"+self.cmd)
self.ids.historybox.text += "\n" + self.cmd
self.cmdexecute(self.cmd.split(str(self.game.current_)+":")[1])
self.ids.cmdbox.text = str(self.game.current_) + ":"
Clock.schedule_once(self.refocus)
def refocus(self, *args):
self.ids.cmdbox.focus = True
...
And I can't say anything about the weird images because I don't get them

Kivy: Popup is blocked by main thread

My Kivy app has a button whose callback involves a UrlRequest. I'd like to provide a popup that askes the user to wait while the request is being completed. The problem is that executing the request itself blocks the popup from coming up. I've tried placing the open() method of the popup at different places with no luck. In the following example, the popup is opened in the on_progress() callback of the UrlRequest:
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.garden.navigationdrawer import NavigationDrawer
from kivy.network.urlrequest import UrlRequest
from kivy.uix.popup import Popup
from kivy.uix.label import Label
import urllib
kv = '''
<ScreenTemplate#SignUpScreen>:
canvas:
Color:
rgb: (0.09,0.65,0.8)
Rectangle:
pos: self.pos
size: self.size
Button:
size_hint: .3, .2
pos_hint: {'center_x': .5,'center_y': .25}
text: 'Call Request'
# on_press: app.p.open()
on_release: app.callback1()
Label:
text: 'This is ' + root.name
font_size: '50sp'
<MyNavDrawer>:
BoxLayout:
orientation: 'vertical'
size_hint_y: .25
pos_hint: {'center_y':.5}
Button:
text: 'Screen 1'
on_press: app.callback2( 'screen1')
Button:
text: 'Screen 2'
on_press: app.callback2('screen2')
SMRoot:
<SMRoot>:
ScreenTemplate:
name: 'screen1'
ScreenTemplate:
name: 'screen2'
'''
Builder.load_string(kv)
class SMRoot(ScreenManager):
pass
class SignUpScreen(Screen):
pass
class myNavDrawer(NavigationDrawer):
pass
class myApp(App):
popup_opened = False
p=Popup(title="Posting request...",
content=Label(text="... Please wait"),
size=(100, 100),
size_hint=(0.5, 0.5),
auto_dismiss = False)
def build(self):
self.mynavdrawer = myNavDrawer()
return self.mynavdrawer
def on_success(self, req, results):
print 'In on_success: '+ results
self.p.title = 'Success'
self.p.content = Label(text=results)
self.p.auto_dismiss = True
popup_opened = False
def on_failure(self, req, results):
self.p.title = 'Failure'
self.p.content = Label(text=results)
print 'In on_failure: '+ results
self.p.auto_dismiss = True
def on_error(self, req, results):
self.p.title = 'Error'
self.p.content = Label(text=results.strerror)
print 'In on_error: '+ results.strerror
self.p.auto_dismiss = True
def on_progress (self, req, results, chunk):
if not self.popup_opened:
print 'In on_progress: '+ str(results)
self.p.open()
self.popup_opened = True
def callback1(self):
params={'show_env':'1'}
params = urllib.urlencode(params)
headers = {'Content-type': 'application/x-www-form-urlencoded',
'Accept': 'text/plain'}
url = 'https://httpbin.org/get'
req = UrlRequest(url,
self.on_success,
req_body = params,
req_headers = headers,
on_failure=self.on_failure,
on_error=self.on_error,
on_progress=self.on_progress,
timeout=4)
req.wait()
print 'After UrlRequest'
if __name__ == '__main__':
myApp().run()
Running this causes the popup to appear after the request is completed, which defeats the purpose.
Notice the commented on_press: app.p.open() binding in the Button under <ScreenTemplate#SignUpScreen>. This works perfectly, except that it's a workaround that is far from ideal. I'd like the popup to be opened anytime the UrlRequest is sent; the above workaround has to be applied to every button.
Any ideas on how to open the button from the main thread while the callback is running would be appreciated. I've tried using Clock.schedule_once() as well. From the docs, it seems EventDispatcher.dispatch() might do the trick, but I don't know what event to dispatch.
I think this line:
req.wait()
blocks you on the main thread.
remove it. you can also open the popup sooner - instead of the req.wait()
That being said, you can replicate this behavior using delayable (from kivyoav)
#delayable
def callback1(self):
...
while not reg.is_finished:
yield 0.02 # sleep for 20 ms...
... #req is ready now...
Disclaimer: I'm the author of kivyoav
I had the same issue as you do. The UrlRequest takes place in the main thread. This means that the UI won't be updated unless the main thread is allowed to run, because it's blocked inside the callback1 function.
What you want to do is create a new thread for the UrlRequest
Try using Threading
I solved a similar problem using threads. Any other attempts finished up the GIL blocking the popup to show or showing it after the operating method. I use def showTheMessage(self): when I want a popup to show until the method in the other thread is done.
# main screen
class MainScreen(Screen):
def __init__(self, **kwargs):
self.name="MAIN SCREEN"
super(Screen, self).__init__(**kwargs)
# popup class
class MsgBox(Popup):
def __init__(self, obj, **kwargs):
super(MsgBox, self).__init__(**kwargs)
self.obj = obj
# app class
class MyApp(App):
def step1(self):
# start the method in a thread
t1 = threading.Thread(target = self.yourBelowedMethod)
t1.start()
t1.join()
# dismiss the message popup once finished
self.popupMsg.dismiss()
def step2(self, *args):
# set the popup structure
self.popupMsg = MsgBox(self)
# run the activity popup in a thread
t3 = threading.Thread(target = self.popupMsg.open)
t3.start()
t3.join()
def showTheMessage(self):
# call step2 and step1
self.step2() # call the message popup
self.step1() # call the method
def build(self):
sm = Builder.load_string("""
ScreenManager
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
size: self.size
MainScreen:
size_hint: 1, 1
auto_dismiss: False
title: "MainScreenTitle"
title_align: "center"
BoxLayout:
orientation: "vertical"
spacing: 10
Label:
text: "MainScreenLabel"
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .8
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .2
Button:
font_size: 50
text: "MessageButtonExit" # exit app
on_press:
self.background_color = 0,255,0,1
app.exit()
<MsgBox>:
size_hint: 1, .7
auto_dismiss: False
title: "yourTitle"
title_align: "center"
title_size: 30
BoxLayout:
orientation: "vertical"
Label:
font_size: '30sp'
text: "yourText"
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
""")
return sm

Categories

Resources