could you tell me how to make clicking the button (MDIconButton) change the icon.
I tried this by changing the icon variable:
class MyButton(MDIconButton):
def __init__(self):
super().__init__(*args, **kwargs)
self.icon = "path to first image"
self.alternative = "path to second image"
self.icon_size = 300
self.radius = 30
self.size_hint = [.05, .05]
def on_press(self):
self.icon, self.alternative = self.alternative, self.icon
But after that the alignment is lost and the icon goes to the bottom left corner and there is no way to change it.
Please help me so much.
The following example contains an MDIconButton, whose icon changes after clicking it, while keeping its original size and position, as intended.
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivy.lang import Builder
Builder.load_string('''
<UpdateIcon>:
orientation: 'vertical'
MDIconButton:
id: iconButton
icon: 'language-python'
pos_hint: {'x':.5, 'y':.5}
size_hint: (.05, .05)
icon_size: '300sp'
on_press: root.updateIcon('android')
''')
class UpdateIcon(BoxLayout):
def __init__(self, **kwargs):
super(UpdateIcon,self).__init__(**kwargs)
pass
def updateIcon(self, newIcon):
self.ids.iconButton.icon = newIcon
class TestApp(MDApp):
def build(self):
self.title = "Change Icon"
return UpdateIcon()
if __name__ == '__main__':
TestApp().run()
Related
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 want to rotate a button with the pure python code instead of the kvlang.
With kvlang we can rotate a button as shown in the example.
The button is rotated 45 degree around its own center. Below is the original code:
from kivy.app import App
from kivy.lang import Builder
kv = '''
FloatLayout:
Button:
text: 'hello world'
size_hint: None, None
pos_hint: {'center_x': .5, 'center_y': .5}
canvas.before:
PushMatrix
Rotate:
angle: 45
origin: self.center
canvas.after:
PopMatrix
'''
class RotationApp(App):
def build(self):
return Builder.load_string(kv)
RotationApp().run()
But when I try to rewrite this example with the pure python code as below, the button is rotated around somewhere else, as shown here:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.graphics import PushMatrix, PopMatrix, Rotate
class MyButton(Button):
def __init__(self):
super().__init__()
self.text = 'hello world'
self.size_hint = (None, None)
self.pos_hint = {'center_x': .5, 'center_y': .5}
with self.canvas.before:
PushMatrix()
Rotate(origin=self.center, angle=45)
with self.canvas.after:
PopMatrix()
class RotationApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.button = MyButton()
self.layout.add_widget(self.button)
def build(self):
return self.layout
RotationApp().run()
The above two pieces of code are not producing the same result. Is there anything we did wrong?
UPDATE:
The puzzle is solved as suggested by #inclement, solution as below:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.graphics import PushMatrix, PopMatrix, Rotate
class MyButton(Button):
def __init__(self):
super().__init__()
self.text = 'hello world'
self.size_hint = (None, None)
self.pos_hint = {'center_x': .5, 'center_y': .5}
with self.canvas.before:
PushMatrix()
# Rotate(origin=self.center, angle=45) # previous approach
self.rotation = Rotate(origin=self.center, angle=45)
self.bind(center=lambda _, value: setattr(self.rotation, "origin", value))
with self.canvas.after:
PopMatrix()
class RotationApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.button = MyButton()
self.layout.add_widget(self.button)
def build(self):
return self.layout
RotationApp().run()
In your Python code, self.center is evaluated just once during the __init__. In the kv code, a binding is automatically created to reset the Rotate instruction's origin property every time it changes.
You need to put that missing functionality in the Python code, something like self.rotation = Rotate(...) and self.bind(center=lambda instance, value: setattr(self.rotation, "origin", value)) (although I'm sure you can think of a nicer way to set that up, that's just the inline example).
I have tried to follow the documentation for this widget:
text_color_normal
and
text_color_active but they don’t work. :/
Can someone please help me :’)
I had the same problem and the only solution is to use ThemeManager.
main.py
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.label import MDLabel
from kivymd.theming import ThemeManager
class Main(MDApp):
"""docstring for Main"""
def __init__(self):
super(Main, self).__init__()
self.screen = Screen()
self.sm = ScreenManager()
self.theme_cls = ThemeManager()
def build(self):
#Enter the desired color instead of red
self.theme_cls.primary_palette = "Red"
if __name__=='__main__':
Main().run()
main.kv
Screen:
ScreenManager:
id: screen_manager
Screen:
name: 'home_screen'
MDBottomNavigation:
panel_color: rgba('262626')
MDBottomNavigationItem:
icon: 'home'
text: 'home'
You can also navigate to kivymd/uix then open bottom navigation. py and customize text_color on line 273 and 289 for icon and label to your desired color eg, 1,1,1,1
I am using Kivy to create a GUI.
If I go back to the Kivy operation after I activate another window, the Kivy button operation is ignored once.
This is a video of the operation.
I tried the following code
#-*- coding: utf-8 -*-
from kivy.lang import Builder
Builder.load_string("""
<TextWidget>:
BoxLayout:
orientation: 'vertical'
size: root.size
TextInput:
text: root.text
Button:
id: button1
text: "Test"
font_size: 48
on_press: root.buttonClicked()
""")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
class TextWidget(Widget):
text = StringProperty()
def __init__(self, **kwargs):
super(TextWidget, self).__init__(**kwargs)
self.text = ''
def buttonClicked(self):
self.text += "test\n"
class TestApp(App):
def __init__(self, **kwargs):
super(TestApp, self).__init__(**kwargs)
self.title = 'greeting'
def build(self):
return TextWidget()
if __name__ == '__main__':
TestApp().run()
Is there a solution to this problem?
I have an application with 1 screen in BoxLayout (filename qmscreens.py).
from kivy.app import App
from kivy.core.window import Window
class qmHome(BoxLayout):
pass
class qmscreensApp(App):
def build(self):
Window.clearcolor = (1,1,1,1)
Window.size = (500, 500)
homeWin = qmHome()
return homeWin
qmscreensApp().run()
The examples below have the changes implemented as suggested.
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
class EditImage(Screen):
pass
class QmHome(Screen):
pass
class QManager(ScreenManager):
elogging = BooleanProperty()
elogging = True
# and a lot of other python code here below
class qmscreensApp(App):
def build(self):
Window.clearcolor = (1,1,1,1)
Window.size = (500, 500)
homeWin = QManager()
return homeWin
qmscreensApp().run()
and the following qmscreens.kv file:
QManager:
QmHome:
EditImage:
<QmHome>:
name: 'home'
Button:
on_press: app.root.current = 'edit'
text: 'to edit screen'
font_size: 50
<EditImage>:
name: 'edit'
Button:
on_release: app.root.current = 'home'
text: 'back to the home screen'
font_size: 50
CheckBox:
id: _evlogg_cb
active: root.elogging
The example above gives the error message:
AttributeError: 'QmHome' object has no attribute 'elogging'
I guess the code line elogging = BooleanProperty() should be changed from the QManager to the QmHome class. But then I get other errors. So I am completely lost. Your help and directions are highly appreciated.
Thanks in advance.
Rename all occurrences of the class qmHome to QmHome. By convention across many different languages, beginning of a class should always start with a capital letter.
Kivy not recognizing the screen named with lowercase seems like a bug.
Few fixes. QManager should be the root widget and you missed a <> in the kv file
def build(self):
Window.clearcolor = (1,1,1,1)
Window.size = (500, 500)
#homeWin = qmHome()
return QManager() # <---- :)
in the kv file:
<QManager>:
qmHome:
EditImage: