kivy: Global variable and ScreenManager - global-variables

I'm using an input from a First Screen, store this input into a global variable CHOSEN_INPUT, then when I go to the Second Screen, this global variable is displayed there.
I've got the beginning but don't know where to go... Maybe with ObjectProperty but I don't know how to pass it from one class to another.
global_test.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager, NoTransition
from kivy.properties import ObjectProperty
#global variable
CHOSEN_INPUT = ''
class FirstScreen(Screen):
obj_input = ObjectProperty()
obj_label = ObjectProperty()
obj_okay = ObjectProperty()
def buttonClicked(self):
global CHOSEN_INPUT
print('RESULT :', self.obj_input.text)
self.obj_label.text = "You wrote : " + self.obj_input.text
CHOSEN_INPUT = self.obj_input.text
class SecondScreen(Screen):
pass
class FromFirstScreen(BoxLayout):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
global CHOSEN_INPUT
self.orientation = 'vertical'
lbl = Label(Text=CHOSEN_INPUT)
self.add_widget(lbl)
class Global_VariableApp(App):
def build(self):
sm = ScreenManager(transition=NoTransition())
sm.add_widget(FirstScreen(name='firstcreen'))
sm.add_widget(SecondScreen(name='secondscreen'))
return sm
if __name__ == "__main__":
Global_VariableApp().run()
and :
global_variable.kv
<FirstScreen>:
obj_input: input_box
obj_label: label_box
obj_okay: okay_btn
BoxLayout:
orientation: 'vertical'
TextInput:
id: input_box
size_hint_y: None
height: "40dp"
multiline: False
Label:
id: label_box
Button:
id: okay_btn
text: "Okay"
on_press: root.buttonClicked()
Button:
text: "Go To Second Screen"
on_press: root.manager.current = 'secondscreen'
<SecondScreen>:
BoxLayout:
orientation: 'vertical'
Button:
text: "Input from First Screen :"
FromFirstScreen:
Button:
text: "Go To First Screen"
on_press: root.manager.current = 'firstcreen'

Found thanks to #mcastle's solution : Changing Kivy widget attribute from another widget
I've put inside the class SecondScreen an update_text function :
class SecondScreen(Screen):
obj_input1_box = ObjectProperty()
def update_text(self, label_text):
#print('label_text :', label_text)
self.obj_input1_box.text = label_text
and in the kv file, I've inserted a function: root.manager.get_screen('secondscreen').update_text(root.obj_label.text)
(and put an id for the label of text of SecondScreen which is updated) :
<FirstScreen>:
...
Button:
id: okay_btn
text: "Okay"
on_press: root.buttonClicked(); root.manager.get_screen('secondscreen').update_text(root.obj_label.text) ...
<SecondScreen>:
obj_input1_box: input1_box
...
Label:
id: input1_box
...

Related

Showing/hiding of a widget on focus at another widget

I would like "Button" object to disappear when "Target" object is not in focus (for example, when object "Secondary" is focused) and to re-appear when "Target" is in focus again. So, "Target" focused = "Button" visible. In other words, in the code below there are two lines, "Line A" and "Line B", that I would like to implement in the code.
`
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit
class Wn(QWidget):
def __init__(self):
super().__init__()
self.target = Target("Target", self)
self.target.setFixedSize(400, 60)
self.target.move(50, 50)
self.secondary = QLineEdit("Secondary", self)
self.secondary.setFixedSize(400, 60)
self.secondary.move(50, 150)
self.button = QPushButton("Appears # Target focused. Disappears # target not focused", self)
self.button.setFixedSize(400, 60)
self.button.move(50, 250)
class Target(QLineEdit):
def focusInEvent(self, k):
print("The target is in focus: Button should be shown")
self.setStyleSheet("background-color: red;")
# Wn.button.setHidden(False) # Line A
def focusOutEvent(self, p):
print("The target is out of focus: Button should be hidden")
self.setStyleSheet("background-color: white;")
# Wn.button.setHidden(True) # Line B
app = QApplication(sys.argv)
wn = Wn()
wn.show()
sys.exit(app.exec())
`
You can create a signal and emit it whenever the focus changes, then connect it with the button's setVisible().
class Wn(QWidget):
def __init__(self):
# ...
self.target.focusChanged.connect(self.button.setVisible)
class Target(QLineEdit):
focusChanged = pyqtSignal(bool)
def focusInEvent(self, k):
super().focusInEvent(k)
print("The target is in focus: Button should be shown")
self.setStyleSheet("background-color: red;")
self.focusChanged.emit(True)
def focusOutEvent(self, p):
super().focusOutEvent(p)
print("The target is out of focus: Button should be hidden")
self.setStyleSheet("background-color: white;")
self.focusChanged.emit(False)
Alternatively, you can just install an event filter on the line edit and look for FocusIn and FocusOut events.
Note that you should always call the base implementation of event handler overrides, unless you really know what you're doing, otherwise you might prevent proper default behavior of the object.
Also, layout managers should always be used instead of fixed geometries. Since the visibility of a widget also nullifies its size in the layout and adapts the other widgets managed by it (similarly to display: none in CSS), you should probably consider using setRetainSizeWhenHidden() for the widget's size policy:
class Wn(QWidget):
def __init__(self):
# ...
# create a proper layout and add widgets
# ...
policy = self.button.sizePolicy()
policy.setRetainSizeWhenHidden(True)
self.button.setSizePolicy(policy)

How to place a button on a loading animation with Kivy?

I'm really new to Kivy, i'm trying to make my first app, but i don't really understand how to play with elements and classes...
I'm trying to put a button to stop the sound but it will just stop the animation....
Here's the code, i think i don't code it properly ! :(
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.animation import Animation
from kivy.properties import NumericProperty
from kivy.core.audio import SoundLoader
from kivy.uix.button import Button
from functools import partial
from kivy.uix.boxlayout import BoxLayout
Builder.load_string('''
<App_container>:
canvas.before:
PushMatrix
Rotate:
angle: root.angle
axis: 0, 0, 1
origin: root.center
canvas.after:
PopMatrix
Image:
id: img_anim
source: 'logo.png'
size_hint: 0,0
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
''')
class App_container(FloatLayout):
angle = NumericProperty(0)
def __init__(self, **kwargs):
#Anim
super(App_container, self).__init__(**kwargs)
anim = Animation(angle = 360, duration=2)
anim2 = Animation(size_hint=(2,2), duration=2)
anim.start(self)
anim2.start(self.ids["img_anim"])
#Son
self.sound = SoundLoader.load('zik.wav')
self.sound.loop = True
self.sound.play()
#boutonzik
btn = Button(text ="Push Me !")
self.add_widget(btn)
btn.bind(on_press=partial(self.foo, btn))
def foo(self, instance, *args):
self.sound.volume=0
class TestApp(App):
def build(self):
return App_container()
if __name__ == "__main__":
app = TestApp()
app.run()
In order for the Button to not spin, it must not be in the spinning Layout. To do this, you can add another FloatLayout in your App_container, and only spin that FloatLayout. The following modification of your code does that:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.animation import Animation
from kivy.core.audio import SoundLoader
Builder.load_string('''
<App_container>:
FloatLayout:
# move the angle property into this FloatLayout
angle: 0.0
id: rotate_this
canvas.before:
PushMatrix
Rotate:
angle: self.angle
axis: 0, 0, 1
origin: root.center
canvas.after:
PopMatrix
Image:
id: img_anim
source: 'logo.png'
size_hint: 0,0
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
Button:
text: 'Push Me'
on_press: root.foo(self)
size_hint: 0.1,0.1
pos_hint: {'center_x':0.5, 'center_y':0.5}
''')
class App_container(FloatLayout):
def __init__(self, **kwargs):
#Anim
super(App_container, self).__init__(**kwargs)
anim = Animation(angle = 360, duration=2)
anim2 = Animation(size_hint=(2,2), duration=2)
# rotate the FloatLayout with id "rotate_this"
anim.start(self.ids["rotate_this"])
# animate the "img_anim"
anim2.start(self.ids["img_anim"])
#Son
self.sound = SoundLoader.load('zik.wav')
self.sound.loop = True
self.sound.play()
def foo(self, instance, *args):
self.sound.volume=0
class TestApp(App):
def build(self):
return App_container()
if __name__ == "__main__":
app = TestApp()
app.run()
So the FloatLayout spins, but the Button does not, since it is not inside the spinning FloatLayout.

Kivy: setting the color of text on a disabled button in kv

I'm trying to set the color of the text on a disabled button separate from the normal color of the button text. As buttons are based on Label i tried to change disabled_color but with out success.
As you can see in the image the font color of the disabled button (left) is still the same as the normal button right. Disabled (left) and normal (right) botton Please help me set the text color correct.
I`m using python 3.7.3 and kivy 1.10.1
Button:
#Set font
font_size: '35sp'
color: 1,1,1,1
disabled_color: 25.0/255.0,25.0/255.0,25.0/255.0,1
#Set background
background_normal: 'button_normal.png'
background_down: 'button_down.png'
background_disabled_normal: 'button_normal_disn.png'
background_disabled_down: 'button_normal_disd.png'
That's strange: disabled_color works fine for me. Are you sure the first button in your image is disabled? Thats my code for testing - maybe it helps.
#!/usr/bin/python3.5
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder
from kivy.properties import ObjectProperty, StringProperty
from kivy.atlas import Atlas
kv= Builder.load_string('''
<MainScreen>
first_but:first_but
orientation:"vertical"
Button:
id: first_but
font_size: '35sp'
color: 1,0,1,1
disabled_color: 100.0/255.0,100.0/255.0,25.0/255.0,1
text: root.enabled
on_release: print("working")
Button:
font_size: '35sp'
color: 1,0,1,1
text: "toggle enablement of upper button"
on_release: root.toggle_enable()
''')
class MainScreen(BoxLayout):
first_but = ObjectProperty(None)
enabled = StringProperty("enabled")
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
def toggle_enable(self):
if self.enabled == "enabled":
self.enabled = "disabled"
self.first_but.disabled = True
else:
self.enabled = "enabled"
self.first_but.disabled = False
class myApp(App):
def build(self):
return MainScreen()
if __name__ == "__main__":
myApp().run()

On a Dialog and MessageDialog how can we change the text of buttons in qml?

Lets suppose we are using a Dialog or a MessageDialog element, where we have the default buttons. How can we change the text and even use qsTr for translating?
Lets suppose the following Dialog
Dialog {
id: dateDialog
visible: true
title: "Choose a date"
standardButtons: StandardButton.Save | StandardButton.Cancel
onAccepted: console.log("Saving the date " +
calendar.selectedDate.toLocaleDateString())
Calendar {
id: calendar
onDoubleClicked: dateDialog.click(StandardButton.Save)
}
}
and MessageDialog:
import QtQuick 2.2
import QtQuick.Dialogs 1.1
MessageDialog {
title: "Overwrite?"
icon: StandardIcon.Question
text: "file.txt already exists. Replace?"
detailedText: "To replace a file means that its existing contents will be lost. " +
"The file that you are copying now will be copied over it instead."
standardButtons: StandardButton.Yes | StandardButton.YesToAll |
StandardButton.No | StandardButton.NoToAll | StandardButton.Abort
Component.onCompleted: visible = true
onYes: console.log("copied")
onNo: console.log("didn't copy")
onRejected: console.log("aborted")
}
As we can see it uses StandardButton this is the one i want the text customizable.
I solved the problem calling the method
myDialog.standardButton(Dialog.Ok).text = qsTrId("Ok")
in Component.onCompleted

Qt text alignment in QCheckBox

I am looking for a way to align text in a QCheckBox to both the right and left side. I cannot seems to find any ways to modify just the alignment of the text and not the checkbox itself.
I don't know if you can access the label associated with the checkbox or not, but if you cannot, a hack would be to set the checkbox label with an empty string and use another QLabel where you can use setAlignment(Qt::AlignJustify) to adjust your text to both the right and left side.
But then I don't know if you consider this modify the checkbox itself and not just the alignement.
I solved it using a trick.
Create a QCheckBox without putting any text in it.
And create a QLable so that you can center the text and click it.
Then it's possible.
Below is the example code.
You can refer to it.
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class MyApp(QWidget):
###### Define StyleSheet ######
DEFAULT_COLOR = {
'border': '#000000',
'hover': '#29adff'
}
CP_DEFAULT_STYLE = '''
QCheckBox::indicator:hover {{
border: 1px solid {hover};
background: white;
}}
QCheckBox::indicator {{
border: 1px solid {border};
background: white;
}}
'''
CP_DEFAULT_STYLE_SET_VALUE = CP_DEFAULT_STYLE.format(**DEFAULT_COLOR)
################################
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.cb = QCheckBox(self)
cp_label = QLabel('Click\nHere Me!!', self)
self.cb.setStyleSheet(self.CP_DEFAULT_STYLE_SET_VALUE)
cp_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
cp_label.setGeometry(75+30, 50+15, 60, 30)
self.cb.move(75+90, 50+20)
self.cb.clicked.connect(self.cpClick)
cp_label.mousePressEvent = self.cpLabelClick
cp_label.leaveEvent = self.cpLabelLeave
cp_label.enterEvent = self.cpLabelEnter
self.setWindowTitle('QCheckBox')
self.setGeometry(300, 300, 300, 200)
self.show()
def cpClick(self):
if not self.cb.isChecked():
self.cb.setStyleSheet(
self.CP_DEFAULT_STYLE_SET_VALUE
)
else:
self.cb.setStyleSheet('')
def cpLabelClick(self, _):
self.cb.setStyleSheet('')
self.cb.setChecked(
not self.cb.isChecked()
)
def cpLabelLeave(self, _):
self.cb.setStyleSheet('')
def cpLabelEnter(self, _):
if not self.cb.isChecked():
setColer = self.DEFAULT_COLOR.copy()
setColer['border'] = self.DEFAULT_COLOR['hover']
self.cb.setStyleSheet(
self.CP_DEFAULT_STYLE.format(**setColer)
)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyApp()
sys.exit(app.exec_())
or
If you use Qt Designer,
After creating one Qwidget, make QCheckBox and QLabel inside
Bring the QLabel to the front and make the QCheckBox deselected.
And if you write it like the code below, it works perfectly!
import os
import sys
from PyQt5.uic import loadUi
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
def resource_path(*relative_Path_AND_File):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = getattr(sys, '_MEIPASS', os.path.dirname(
os.path.abspath(__file__)))
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, '/'.join(relative_Path_AND_File))
class MyApp_Define:
cb: QCheckBox
cb_label: QLabel
class MyApp(QMainWindow, MyApp_Define):
###### Define StyleSheet ######
DEFAULT_COLOR = {
'border': '#000000',
'hover': '#29adff'
}
CP_DEFAULT_STYLE = '''
QCheckBox::indicator:hover {{
border: 1px solid {hover};
background: white;
}}
QCheckBox::indicator {{
border: 1px solid {border};
background: white;
}}
'''
CP_DEFAULT_STYLE_SET_VALUE = CP_DEFAULT_STYLE.format(**DEFAULT_COLOR)
################################
def __init__(self):
super().__init__()
loadUi(resource_path("TEST.ui"), self)
self.cb_label.installEventFilter(self)
self.cb_label.leaveEvent = self.cbLabelLeave
self.cb_label.mousePressEvent = self.cbLabelClick
def eventFilter(self, source: QObject, event: QEvent):
# 자동 종료 체크박스 안에 들어갈시
if (
source is self.cb_label and
event.type() == QEvent.Type.MouseMove and
not self.cb.isChecked()
):
self.cbLabelEnter()
return super().eventFilter(source, event)
def cbLabelClick(self, _):
self.cb.setStyleSheet('')
self.cb.setChecked(
not self.cb.isChecked()
)
if not self.cb.isChecked():
self.cbLabelEnter()
def cbLabelLeave(self, _):
self.cb.setStyleSheet('')
def cbLabelEnter(self, _=None):
if not self.cb.isChecked():
setColer = self.DEFAULT_COLOR.copy()
setColer['border'] = self.DEFAULT_COLOR['hover']
self.cb.setStyleSheet(
self.CP_DEFAULT_STYLE.format(**setColer)
)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyApp()
ex.show()
sys.exit(app.exec_())
The image below is an example of QtDesigner.

Resources