I am working with a chat app and I would like the server to control to flush the data to clients or not by pressing a button. I tried to do that by updating a global variable to trigger a if statement that can flush the data. However it seems that even the global variable is updated, it still does not trigger the if statement as the debugging "print" is not working, how can i fix that?
Here is the code
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.scatter import Scatter
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.graphics.vertex_instructions import Rectangle
from kivy.graphics.context_instructions import Color
from kivy.graphics.instructions import Instruction
from kivy.base import runTouchApp
from kivy.lang import Builder
import socket
from kivy.core.window import Window
import pygame
import random
from kivy.support import install_twisted_reactor
install_twisted_reactor()
from twisted.internet import reactor, protocol, defer
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import Protocol, Factory
censored = 0
class MultiClientEcho(protocol.Protocol):
def __init__(self, factory, app):
self.factory = factory
self.app = app
def connectionMade(self):
self.factory.clients.append(self)
def dataReceived(self, data):
for client in self.factory.clients:
storedmessage = self.factory.app.handle_message(data)
global censored
if censored ==1:
print "send message"
client.transport.write(storedmessage)
elif censored ==2:
print "banned message"
client.transport.write("censored")
def connectionLost(self,reason):
self.factory.clients.remove(self)
class MultiClientEchoFactory(protocol.Factory):
protocol = MultiClientEcho
def __init__(self,app):
self.clients = []
self.app = app
def buildProtocol(self, addr):
return MultiClientEcho(self, self.app)
class ServerApp(App):
def build(self):
self.label = Label(text="server started\n")
self.approve_btn = Button(text="approve")
self.approve_btn.bind(on_release=self.send_message)
self.banned_btn = Button(text="banned")
self.banned_btn.bind(on_release=self.banned_message)
self.layout = BoxLayout(orientation='vertical', spacing=10)
reactor.listenTCP(8000, MultiClientEchoFactory(self))
self.layout.add_widget(self.label)
self.layout.add_widget(self.approve_btn)
self.layout.add_widget(self.banned_btn)
return self.layout
def send_message(self, *args):
global censored
censored = 1
self.label.text += "sent\n"
print censored
return censored
def banned_message(self, *args):
global censored
censored = 2
self.label.text += "censored\n"
print censored
return censored
def handle_message(self, msg):
self.label.text += "received: %s\n" % msg
return msg
if __name__ == '__main__':
ServerApp().run()
Related
I am trying to do an asynchronous label update in Kivi gui.
When I run the function via loop.run_until_complete(self.update()) I get only output to the console, the application window is not updated. What am I missing?
# async kivy app example
import kivy
kivy.require('2.1.0') # replace with your current kivy version !
from kivy.app import App
from kivy.app import async_runTouchApp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
import asyncio, time
class RootWidget(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
btn = Button(text='Run Async func')
btn.fbind('on_press', self.async_callback)
self.lbl = Label(text = 'Click on the button!')
self.add_widget(btn)
self.add_widget(self.lbl)
async def update(self):
async for i in async_func():
print(i)
self.lbl.text = str(i)
def async_callback(self, *args):
loop = asyncio.get_event_loop()
loop.run_until_complete(self.update())
async def async_func():
for i in range(0,100):
yield i
await asyncio.sleep(1)
class AsyncApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(async_runTouchApp(AsyncApp().run(), async_lib='asyncio'))
loop.close()
when I press on a button i would like run a function everey minute.
I am using sched.schedulerbut my kivy app crash.
s.enter(60, 1, self.graph_data, (s,))
s.run()
Someone can help me ?
You can use the Clock object provided by kivy:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
class TestApp(App):
def build(self):
self.timer = None
btn1 = Button(text='Click to start')
btn1.bind(state=self.callbackStart)
btn2 = Button(text='Click to stop')
btn2.bind(state=self.callbackStop)
bl = BoxLayout()
bl.add_widget(btn1)
bl.add_widget(btn2)
return bl
def callbackStart(self, instance, value):
print('My button1 <%s> state is <%s>' % (instance, value))
if self.timer is not None:
Clock.unschedule(self.timer)
self.timer = Clock.schedule_interval(self.timedAction, 0.5)
def callbackStop(self, instance, value):
print('My button2 <%s> state is <%s>' % (instance, value))
Clock.unschedule(self.timer)
def timedAction(self, dt):
print("Repeat")
if __name__ == '__main__':
TestApp().run()
You should also be able to do something like this by using the threading build-in module with the Thread and Event objects.
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
import threading
class MyTimedAction(threading.Thread):
def __init__(self, event):
threading.Thread.__init__(self)
self.stopped = event
def run(self):
while not self.stopped.wait(0.5):
print("Periodic action performed")
class TestApp(App):
def build(self):
# Create the stopTimer event. Initialize as stopped.
self.stopTimer = threading.Event()
self.stopTimer.set()
btn1 = Button(text='Click to start')
btn1.bind(state=self.callbackStart)
btn2 = Button(text='Click to stop')
btn2.bind(state=self.callbackStop)
bl = BoxLayout()
bl.add_widget(btn1)
bl.add_widget(btn2)
return bl
def callbackStart(self, instance, value):
print('My button1 <%s> state is <%s>' % (instance, value))
if self.stopTimer.isSet(): # (Eventually) avoid duplicates if timer already started
self.stopTimer.clear()
thread = MyTimedAction(self.stopTimer)
thread.start()
def callbackStop(self, instance, value):
print('My button2 <%s> state is <%s>' % (instance, value))
self.stopTimer.set()
if __name__ == '__main__':
TestApp().run()
The examples above assumes that if the timer is already started it doesn't start again another timed job on a new thread.
The brief GUI code is just for demo purpose.
I'd like to display numeric values coming from a sensor (Tinkerforge ambient light sensor) in Kivy widgets. Unfortunately the variable 'illuminance' doesn't seem to change at all and the widgets display '0'.'illuminance' seems to update nicely and gets printed on the console. What am I doing wrong?
import kivy
import random
from kivy.clock import Clock
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from tinkerforge.ip_connection import IPConnection
from tinkerforge.brick_dc import BrickDC
from tinkerforge.bricklet_ambient_light import BrickletAmbientLight
HOST = "localhost" # Tinkerforge IP and port
PORT = 4223
UID = "uM9" # sensor ID
illuminance = 0
ipcon = IPConnection() # Create IP connection
al = BrickletAmbientLight(UID, ipcon) # Create device object
ipcon.connect(HOST, PORT) # Connect to brickd
class TimerTink:
def tinker(self):
illuminance = al.get_illuminance() #read sensor value
print(illuminance)
class TinkerApp(App):
def build(self):
main_layout = BoxLayout(padding=10, orientation="vertical")
for i in range(2):
h_layout = BoxLayout(padding=10)
for i in range(2):
lbl = Label(text=str(illuminance),)
h_layout.add_widget(lbl)
main_layout.add_widget(h_layout)
event = Clock.schedule_interval(TimerTink.tinker, 1/2)
return main_layout
if __name__ == "__main__":
app = TinkerApp()
app.run()
Finally it's working, thanks to #YOSHI 's suggestions:
import kivy
import random
from kivy.clock import Clock
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from tinkerforge.ip_connection import IPConnection
from tinkerforge.brick_dc import BrickDC
from tinkerforge.bricklet_ambient_light import BrickletAmbientLight
HOST = "localhost" # Tinkerforge IP and port
PORT = 4223
UID = "uM9" # sensor ID
illuminance = 0
ipcon = IPConnection() # Create IP connection
al = BrickletAmbientLight(UID, ipcon) # Create device object
ipcon.connect(HOST, PORT) # Connect to brickd
class TinkerApp(App):
def build(self):
i = 0
main_layout = BoxLayout(padding=10, orientation="vertical")
h_layout = FloatLayout(size=(300,300))
self.label = Label(text=str(illuminance),pos=(i*100, i*100),size_hint = (.1,.1))
h_layout.add_widget(self.label)
main_layout.add_widget(h_layout)
Clock.schedule_interval(self.timer, 0.1)
return main_layout
def timer(self, dt):
illuminance = al.get_illuminance() #read sensor value
self.label.text = str(illuminance)
print(str(illuminance))
if __name__ == "__main__":
app = TinkerApp()
app.run()
The function 'tinker' in the TimeTink class takes 2 arguments: self and dt
def tinker(self,dt):
illuminance = al.get_illuminance() #read sensor value
print(illuminance)
or you change the line where you call the method to sth. like this:
event = Clock.schedule_interval(lambda dt: TimerTink.tinker, 1/2)
for more info visit the doc for kivy.clock.
Edit: (maybe this works)
import kivy
import random
from kivy.clock import Clock
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from tinkerforge.ip_connection import IPConnection
from tinkerforge.brick_dc import BrickDC
from tinkerforge.bricklet_ambient_light import BrickletAmbientLight
HOST = "localhost" # Tinkerforge IP and port
PORT = 4223
UID = "uM9" # sensor ID
illuminance = 0
ipcon = IPConnection() # Create IP connection
al = BrickletAmbientLight(UID, ipcon) # Create device object
ipcon.connect(HOST, PORT) # Connect to brickd
main_layout = BoxLayout(padding=10, orientation="vertical")
for i in range(2):
h_layout = BoxLayout(padding=10)
for i in range(2):
lbl = Label(text=str(illuminance),)
h_layout.add_widget(lbl)
main_layout.add_widget(h_layout)
def tinker(self):
illuminance = al.get_illuminance() #read sensor value
lbl.text = str(illuminance)
print(illuminance)
class TinkerApp(App):
def build(self):
event = Clock.schedule_interval(tinker, 1/2)
return main_layout
if __name__ == "__main__":
app = TinkerApp()
app.run()
I am writing code for a heating control system.
I just want to be able to change the label texts FROM WITHIN PYTHON.
By that I mean, NOT in the GUI code, but somewhere else in the main.
Here is my MWE:
import time
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
import multiprocessing
from kivy.properties import StringProperty
class Data (Widget):
top = StringProperty('hiii')
def __init__(self, **kwargs):
super(Widget, self).__init__(**kwargs)
global mydata
mydata=Data()
class myw (BoxLayout):
def __init__(self, **kwargs):
super(myw, self).__init__(**kwargs)
VERT = BoxLayout(orientation='vertical')
o = Label(text='Oben: ',
font_size=120)
m = Label(text='Mitte: ',
font_size=120)
u = Label(text='Unten: ',
font_size=120)
a = Label(text='Aussen: ',
font_size=120)
mydata.bind(top=o.setter('text'))
VERT.add_widget(o)
VERT.add_widget(m)
VERT.add_widget(u)
VERT.add_widget(a)
onoff = Button(text='Ein',
font_size=120,
size_hint=(0.3, 1))
self.add_widget(VERT)
self.add_widget(onoff)
class TutorialApp(App):
def build(self):
return myw()
if __name__ == "__main__":
try:
global myapp
myapp=TutorialApp()
app_runner=multiprocessing.Process(target=myapp.run)
app_runner.start()
time.sleep(3)
mydata.top='new value assigned'
print (mydata.top)
time.sleep(5)
app_runner.terminate()
except Exception as e:
print ('error occured', e)
I deliberately declared the variable 'mydata' outside the kivy code, such that i can access it from elsewhere in the code (not shown here).
Using threading instead of multiprocessing solved the problem.
So instead of
app_runner=multiprocessing.Process(target=myapp.run)
it now reads:
app_runner=threading.Thread(target=myapp.run)
I'd like to have a kivy app function as a launcher for other kivy apps, depending on input. The way I implemented it below is obviously not working (because the kv file gets reloaded and its styles reapplied, thus adding more and more buttons), and there also seems to be some recursion as the trace suggests when I hit Esc to exit.
I do get the warning that app1.kv is loaded multiple times, however, in the documentation for App.load_kv() it says
This method is invoked the first time the app is being run if no
widget tree has been constructed before for this app.
This implies to me that it should be possible to run() an app multiple times?
Here is my code:
main.py
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.lang import Builder
class OutsideApp(App):
current_app = ObjectProperty(None)
def build(self):
Clock.schedule_interval(self.update, 3)
return Widget()
def update(self, dt):
if isinstance(self.current_app, App):
self.current_app.stop()
if isinstance(self.current_app, App1):
self.current_app = App2()
else:
self.current_app = App1()
self.current_app.run()
class App1(App):
pass
class App2(App):
def build(self):
gl = Builder.load_string("<SequencesGame#GridLayout>:\n cols: 2\n Button:\n text: \"hello 2\"\nSequencesGame:")
return gl
if __name__ == '__main__':
oa = OutsideApp()
oa.run()
app1.kv
#:kivy 1.0.9
<SequencesGame#GridLayout>:
cols: 2
Button:
text: "hello 111"
SequencesGame:
This seems to be an issue even if apps are not nested:
main2.py
from kivy.app import App
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.lang import Builder
class App1(App):
pass
class App2(App):
def build(self):
return Builder.load_string("<SequencesGame#GridLayout>:\n cols: 2\n Button:\n text: \"hello 2\"\nSequencesGame:")
current_app = None
def switch(*args):
global current_app
if isinstance(current_app, App):
current_app.stop()
if isinstance(current_app, App1):
current_app = App2()
else:
current_app = App1()
current_app.run()
if __name__ == '__main__':
Clock.schedule_interval(switch, 2)
switch()
In the end I followed #inclement's suggestion of using multiprocessing. Here is a basic implementation (without communication through Pipes or Queues):
import sys
import multiprocessing
from kivy.app import App
from kivy.lang import Builder
from time import sleep#, localtime, time
def str_to_class(str):
return reduce(getattr, str.split("."), sys.modules[__name__])
class App1(App):
def build(self):
return Builder.load_string("BoxLayout:\n Button:\n text: \"hello 1\"")
class App2(App):
def build(self):
return Builder.load_string("BoxLayout:\n Button:\n text: \"hello 2\"")
class Wrapper(object):
def __init__(self, *kargs, **kwargs):
super(Wrapper, self).__init__()
self.running_app = None
self.next_app = 'App1'
def change_running_app(self, name='App1'):
if self.running_app is not None:
self.running_app.terminate()
self.running_app = multiprocessing.Process(target=self.run_app, kwargs={'name':name})
self.running_app.start()
def run_app(self, name='App1'):
app = str_to_class(name)()
app.run()
def swap(self):
self.change_running_app(name=self.next_app)
self.next_app = ['App1', 'App2']['App1' is self.next_app]
if __name__ == '__main__':
counter = 0
w = Wrapper()
while True:
counter += 1
print "Started App instance {}".format(counter)
w.swap()
sleep(5)