How to highlight focus switch widget in kivy - python

Can someone tell me how to show highlight switch widget when I set focus on Switch?It should be look like focused.
test.py
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.core.window import Window
from kivy.properties import StringProperty, ObjectProperty
from kivy.clock import Clock
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (200, 150)
class User(Screen):
swtch = ObjectProperty(None)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
Clock.schedule_once(self.swtch_focus, 1)
def swtch_focus(self, *args):
self.swtch.focus = True
class Test(App):
def build(self):
return self.root
if __name__ == '__main__':
Test().run()
test.kv
User:
swtch: swtch
BoxLayout:
orientation: "vertical"
GridLayout:
cols: 2
padding: 20, 20
spacing: 10, 10
Switch:
id:swtch

There is hoverableBehaviour in github here is the link https://gist.github.com/KeyWeeUsr/3166f13d363d5558f481d538c6a5d2aa copy the code and create a module to your current directory and import it to use it which you can add to your Widget to have the on focus or highlight feature. This is how to do.
class Highlight(HoverBehavior,Screen):
def on_enter(self):
print('Focus on')
def on_leave(self):
print('Focus off')

Related

kivy load camera (zbarscan) on click button

I've just started my first kivy app. The app is intended to start with button "Start Scan" and then show up the QR scanner built with ZBarCam.
I'm using Screens with the ScreenManager to change from the button view to the camera view (with zbarcam), the problem is that I realized that the camera is initialized from the beginning , so before clicking on the button the camera is already on (I know it because the led from the camera is on).
I don't know if Screen should not be used in this case, or if is there a way to tell the app to do not initialize all screens.
The code I'm using is the following:
QrApp.py:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class QrReader(Screen):
pass
class ScanButton(Screen):
pass
class QrApp(App):
pass
if __name__ == '__main__':
QrApp().run()
qrapp.kv:
ScreenManager:
id: screen_manager
ScanButton:
id: scan_btn
name: 'scan_btn'
manager: 'screen_manager'
QrReader:
id: qr_reader
name: 'qr_reader'
manager: 'screen_manager'
<ScanButton>:
BoxLayout:
orientation: 'vertical'
Button:
text:'Start Scan'
font_size:"50sp"
color: [0, 255, 255, .67]
on_press: app.root.current = 'qr_reader'
<QrReader>:
#:import ZBarCam kivy_garden.zbarcam.ZBarCam
BoxLayout:
orientation: 'vertical'
ZBarCam:
id:qrcodecam
Label:
size_hint: None, None
size: self.texture_size[0], 50
text: ' '.join([str(symbol.data) for symbol in qrcodecam.symbols])
Thanks!
==== ALTERNATIVE BASED IN A COMMENT (still fails) ====
Based on the comment from n4321d I tried to add the ZBarCam as widget in the QrReader Screen. While I can now initiate the camera when the widget is added, I don't see how I can get the symbols that is, the text read from the QR.
This alternative code is the following:
QrApp.py:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
class QrReader(Screen):
def on_enter(self):
from kivy_garden.zbarcam import ZBarCam
zbarcam = ZBarCam()
self.add_widget(zbarcam)
self.add_widget(Label(
text='PRINT SYMBOLS', #' '.join([str(symbol.data) for symbol in zbarcam.symbols] does not work
size_hint=(None,None),
size=(Window.width*0.1, Window.height*0.1),
center=(Window.width*0.3, Window.height*0.5)))
class ScanButton(Screen):
pass
class QrApp(App):
pass
if __name__ == '__main__':
QrApp().run()
qrapp.kv
ScreenManager:
id: screen_manager
ScanButton:
id: scan_btn
name: 'scan_btn'
manager: 'screen_manager'
QrReader:
id: qr_reader
name: 'qr_reader'
manager: 'screen_manager'
<ScanButton>:
BoxLayout:
orientation: 'vertical'
Button:
text:'Start Scan'
font_size:"50sp"
color: [0, 255, 255, .67]
on_press:
app.root.current = 'qr_reader'
<QrReader>:
BoxLayout:
orientation: 'vertical'
====== SOLUTION ========
My workaround solution is posted as an answer to this question here
First:
I would also add an on_leave function where you remove your cam widget otherwise you will keep adding new widgets every time you load it.
I dont have a working cam now , so i cannot test your code. Looking at your code I think you have to bind the text in your Label to the text in zbarcam.symbols with a function: self.label = Label(....); zbarcam.bind(symbols=lambda *x: setattr(self.label, "text", str(x[1]))) or somethign like that.
Here is an example using a random text generator instead of ZBarCam (since i cannot run that).
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ListProperty
import random
from kivy.clock import Clock
kv_str = """
ScreenManager:
id: screen_manager
ScanButton:
id: scan_btn
name: 'scan_btn'
manager: 'screen_manager'
QrReader:
id: qr_reader
name: 'qr_reader'
manager: 'screen_manager'
<ScanButton>:
BoxLayout:
orientation: 'vertical'
Button:
text:'Start Scan'
font_size:"50sp"
color: [0, 255, 255, .67]
on_press:
app.root.current = 'qr_reader'
<QrReader>:
BoxLayout:
orientation: 'vertical'
"""
class ZBarCam(Label):
symbols = ListProperty([])
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_interval(self.gen_rand_text, 1)
def gen_rand_text(self, *args):
self.text = random.choice(['aaaaa', 'bbbbb', 'ccccc'])
self.symbols.append(self.text)
if len(self.symbols) > 3:
del self.symbols[0]
class QrReader(Screen):
def on_enter(self):
self.zbarcam = ZBarCam()
self.add_widget(self.zbarcam)
self.label = Label(
text='PRINT SYMBOLS', #' '.join([str(symbol.data) for symbol in zbarcam.symbols] does not work
size_hint=(None,None),
size=(Window.width*0.1, Window.height*0.1),
center=(Window.width*0.3, Window.height*0.5))
self.add_widget(self.label)
self.zbarcam.bind(symbols = lambda *x: setattr(self.label, "text", str(x[1])))
def on_leave(self, *args):
self.remove_widget(self.zbarcam)
self.remove_widget(self.label)
class ScanButton(Screen):
pass
class QrApp(App):
def build(self):
return Builder.load_string(kv_str)
if __name__ == '__main__':
QrApp().run()
if it still does not work you might have to call self.zbarcam.start() too in the on_enter method after adding it
hope this helps
Thanks to answer from n4321d I finally found a solution.
I also struggled releasing the camera, as zbarcam.stop() stops zbarcam but camera was apparently still open (led was on). After digging a lot, I found this workaround which seems to work well to release the camera.
I also merged the button and qr reader in the same Class and I no longer use kv file, in this example. The final code is the following:
QrApp.py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy_garden.zbarcam import ZBarCam
class QrScanner(BoxLayout):
def __init__(self, **kwargs):
super(QrScanner, self).__init__(**kwargs)
btn1 = Button(text='Scan Me', font_size="50sp")
btn1.bind(on_press=self.callback)
self.add_widget(btn1)
def callback(self, instance):
"""On click button, initiate zbarcam and schedule text reader"""
self.remove_widget(instance) # remove button
self.zbarcam = ZBarCam()
self.add_widget(self.zbarcam)
Clock.schedule_interval(self.read_qr_text, 1)
def read_qr_text(self, *args):
"""Check if zbarcam.symbols is filled and stop scanning in such case"""
if(len(self.zbarcam.symbols) > 0): # when something is detected
self.qr_text = self.zbarcam.symbols[0].data # text from QR
Clock.unschedule(self.read_qr_text, 1)
self.zbarcam.stop() # stop zbarcam
self.zbarcam.ids['xcamera']._camera._device.release() # release camera
class QrApp(App):
def build(self):
return QrScanner()
if __name__ == '__main__':
QrApp().run()
There is no way to lazy-load kivy Screens. Try this as a workaround: Do not let ScreenManager know about all Screens. Use switch_to to remove the current screen and load the desired one instead. Check Kivy docs to learn how to use it.
Maybe so?
from kivy_garden.zbarcam import ZBarCam
from kivymd.uix.textfield import MDTextField
class MenuScreen(MDScreen):
zbarcam: ZBarCam = ObjectProperty()
qrfield: MDTextField = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.__cam_release()
def scan(self, instance):
self.__cam_release()
def on_touch_down(self, touch):
if self.zbarcam.collide_point(*touch.pos):
self.__cam_open()
return super().on_touch_down(touch)
def __cam_release(self):
if self.zbarcam.xcamera._camera._device.isOpened():
self.zbarcam.stop()
self.zbarcam.xcamera._camera._device.release()
def __cam_open(self):
if not self.zbarcam.xcamera._camera._device.isOpened():
self.zbarcam.xcamera._camera._device.open(0)
self.zbarcam.start()

I can't replace a Widget with a Screen in kivy

I'm a beginner at kivy, I have an fclass(Widget) that I want it to be a fclass(Screen), but when I tried to make the change all the screen messed up, the code generate some buttons with a for loop, I wish I coud do the same with a float layout, but I want the fclass to stay a screen since I'm building a multiscreen app.
Here is the .py file:
import kivy
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.core.window import Window
from kivy.config import Config
from kivy.lang import Builder
from kivy.properties import StringProperty, ObjectProperty, NumericProperty,ReferenceListProperty
from kivy.graphics.texture import Texture
from kivy.core.camera import Camera
from kivy.graphics import *
import time
import os
from pathlib import Path
#import cv2
import struct
import threading
import pickle
Builder.load_file('the.kv')
class fscreen(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.list_of_btns = []
def create(self):
self.h = self.height*0.9
for i in range(4):
self.h = self.h - self.height*0.1
self.btn = Button(text='button '+str(i), size=(self.width*0.4,self.height*0.05), pos=(self.width*0.3, self.h), on_press=self.press)
self.list_of_btns.append(self.btn)
self.add_widget(self.btn)
def press(self, instance):
print(instance.text)
def delete(self):
for btns in self.list_of_btns:
self.remove_widget(btns)
class theapp(App):
def build(self):
self.screenm = ScreenManager()
self.fscreen = fscreen()
screen = Screen(name = "first screen")
screen.add_widget(self.fscreen)
self.screenm.add_widget(screen)
return self.screenm
if __name__ == "__main__":
theapp = theapp() #
theapp.run()
The .kv file:
<fscreen>
Button:
text: 'create'
size: root.width*0.4, root.height*0.05
pos: root.width*0.3, root.height*0.1
on_press: root.create()
Button:
text: 'delete'
size: root.width*0.4, root.height*0.05
pos: root.width*0.3, root.height*0.2
on_press: root.delete()
How can I make the fclass a screen class without messing up everything ?
Thank you in advance
Seems to me that code should work, but it doesn't. A fix is to use size_hint instead of size in both your kv and py. So the kv could look like:
<fscreen>:
Button:
text: 'create'
size_hint: 0.4, 0.05
# size: root.width*0.4, root.height*0.05
pos: root.width*0.3, root.height*0.1
on_press: root.create()
Button:
text: 'delete'
size_hint: 0.4, 0.05
# size: root.width*0.4, root.height*0.05
pos: root.width*0.3, root.height*0.2
on_press: root.delete()
and in the create() method:
def create(self):
self.h = self.height * 0.9
for i in range(4):
self.h = self.h - self.height * 0.1
self.btn = Button(text='button ' + str(i), size_hint=(0.4, 0.05),
pos=(self.width * 0.3, self.h), on_press=self.press)
self.list_of_btns.append(self.btn)
self.add_widget(self.btn)
Your size in the kv and py were both just trying to do the size_hint anyway.
And, of course, your build() method must be adjusted:
def build(self):
self.screenm = ScreenManager()
self.fscreen = fscreen(name="first screen")
self.screenm.add_widget(self.fscreen)
return self.screenm
Other things to note:
You should use upper case for class names. Failure to do so can lead to errors in kv
You should consider using pos_hint instead of pos to allow better resizing of your App

Kivy button text alignment (halign) doesn't work

I want to align the text of the button to the left with halign='left' in Kivy, but it does not work and keeps it centered.
This is my code:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.cols = 1
for i in range (0,10):
self.btn = Button(text=str(i),halign='left',background_color =(.3, .6, .7, 1))
self.btn.bind(on_press=self.pressed)
self.add_widget(self.btn)
def pressed(self, instance):
print ("Name:",instance.text)
class MyApp(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
MyApp().run()
According to the documentation for halign:
This doesn’t change the position of the text texture of the Label
(centered), only the position of the text in this texture. You
probably want to bind the size of the Label to the texture_size or set
a text_size.
So, to get the result you want, you can define an extension to Button that does what the above documentation suggests:
class MyButt(Button):
pass
Builder.load_string('''
<MyButt>:
halign: 'left'
text_size: self.size
background_color: (.3, .6, .7, 1)
''')
Then use that class in your App:
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.cols = 1
for i in range (0,10):
self.btn = MyButt(text=str(i))
self.btn.bind(on_press=self.pressed)
self.add_widget(self.btn)

How to remove a dynamically added BoxLayout in Kivy

I've asked in past questions how to add and remove buttons dynamically.
I want to know how to dynamically delete BoxLayout with Python's Kivy.
Here is my code.
#-*- coding: utf-8 -*-
from kivy.config import Config
from kivy.uix.button import Button
Config.set('graphics', 'width', 300)
Config.set('graphics', 'height', 300)
Config.set('input', 'mouse', 'mouse,multitouch_on_demand') # eliminate annoying circle drawing on right click
from kivy.lang import Builder
Builder.load_string("""
<AddItemWidget>:
BoxLayout:
size: root.size
orientation: 'vertical'
RecycleView:
size_hint: 1.0,1.0
BoxLayout:
id: box
orientation: 'vertical'
Button:
id: addButton
text: "Add Item"
on_press: root.buttonClicked()
""")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
class RemovableButton(Button):
def on_touch_down(self, touch):
if touch.button == 'right':
if self.collide_point(touch.x, touch.y):
self.parent.remove_widget(self)
return True
return super(RemovableButton, self).on_touch_down(touch)
class AddItemWidget(Widget):
def __init__(self, **kwargs):
super(AddItemWidget, self).__init__(**kwargs)
self.count = 0
def buttonClicked(self):
self.count += 1
boxLayout = BoxLayout()
textinput = TextInput(text='Hello world'+str(self.count),size_hint_x=0.8)
deleteButton = RemovableButton(text='×',size_hint_x=0.2)
boxLayout.add_widget(deleteButton, index=1)
boxLayout.add_widget(textinput, index=1)
self.ids.box.add_widget(boxLayout, index=1)
deleteButton.bind(on_release=boxLayout.remove_widget)
class TestApp(App):
def __init__(self, **kwargs):
super(TestApp, self).__init__(**kwargs)
def build(self):
return AddItemWidget()
if __name__ == '__main__':
TestApp().run()
When the above code is executed, GUI will be launched and a line will be added by pressing the "Add Item" button.
I want to remove the line, so with the "x" button, as in the image below.
Its a little different than your other example.
For this, you need to use a different function to do the removing.
You also have to import partial like this: from functools import partial
And here is the changed AddItemWidget class:
class AddItemWidget(Widget):
def __init__(self, **kwargs):
super(AddItemWidget, self).__init__(**kwargs)
self.count = 0
def buttonClicked(self):
self.count += 1
boxLayout = BoxLayout()
textinput = TextInput(text='Hello world'+str(self.count),size_hint_x=0.8)
deleteButton = RemovableButton(text='×',size_hint_x=0.2)
boxLayout.add_widget(deleteButton, index=1)
boxLayout.add_widget(textinput, index=1)
self.ids.box.add_widget(boxLayout, index=1)
deleteButton.bind(on_release=partial(self.remove_btn, boxLayout)) # change this
def remove_btn(self, boxLayout, *args): # and add this
self.ids.box.remove_widget(boxLayout)

Anchor Layout to show many widgets in a screen at relative positions

I want to show in a button and a label in a widget at left,center position and right,bottom without using .kv code . Here is my code and i am not able to figure out how to do it Can someone give advice ?
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
class Container(AnchorLayout):
def __init__(self, **kwargs):
super(Container, self).__init__(**kwargs)
btn = Button(text='Hello World',anchor_x='right',anchor_y='bottom')
self.add_widget(btn)
lbl = Label(text="Am i a Label ?",anchor_x='left',anchor_y='center')
self.add_widget(lbl)
class MyJB(App):
def build(self):
parent = Container()
return parent
if __name__ == '__main__':
MyJB().run()
The AnchorLayout aligns all widgets to a given point, not each widget to its own point. If you want widgets anchored in different locations, you need to use multiple AnchorLayouts. You also probably want to specify size and size_hint on either the AnchorLayout or the content widgets.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
class Container(FloatLayout):
def __init__(self, **kwargs):
super(Container, self).__init__(**kwargs)
anchor_rb = AnchorLayout(anchor_x='right', anchor_y='bottom')
btn = Button(text='Hello World', size=(100, 100), size_hint=(None, None))
anchor_rb.add_widget(btn)
self.add_widget(anchor_rb)
anchor_lc = AnchorLayout(anchor_x='left', anchor_y='center')
lbl = Label(text="Am i a Label ?", size=(100, 100), size_hint=(None, None))
anchor_lc.add_widget(lbl)
self.add_widget(anchor_lc)
class MyJB(App):
def build(self):
parent = Container()
return parent
if __name__ == '__main__':
MyJB().run()
Personally, I find kv to be cleaner than manual widget creation, and it helps provide a definite separation between the UI and behavior.
kv version:
from kivy.app import App
from kivy.lang import Builder
root = Builder.load_string('''
FloatLayout:
AnchorLayout:
anchor_x: 'right'
anchor_y: 'bottom'
Button:
text: 'Hello World'
size: 100, 100
size_hint: None, None
AnchorLayout:
anchor_x: 'left'
anchor_y: 'center'
Label:
text: 'Am i a Label ?'
size: 100, 100
size_hint: None, None
''')
class MyJB(App):
def build(self):
return root
if __name__ == '__main__':
MyJB().run()

Categories

Resources