I'm new to Kivy. I'm working on this code and I'm getting confused about what the bind function does.
Basically, the code below generates a text input and prints out the user's input.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
class LoginScreen(Widget):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.username = TextInput(size = (300, 30), pos = (300, 30), multiline = False)
# self.username.bind(on_text_validate = self.on_enter) ### first line
self.username.bind(text= self.on_text) ### second line
self.add_widget(self.username)
def on_enter(instance, value, secondvalue):
print(secondvalue)
def on_text(instance, value, secondvalue):
print(secondvalue)
class ABCApp(App):
def build(self):
return LoginScreen()
if __name__ == "__main__":
ABCApp().run()
Here's what I'm confused about. Why is it that only by printing out the secondvalue will I get the user's actual input? What is the bind function doing here? I looked at the documentation but couldn't find anything.
Also if I switch the commenting about such that the first line is commented out and the second line commented in, such that
self.username.bind(on_text_validate = self.on_enter) ### first line
# self.username.bind(text= self.on_text) ### second line
I am now referencing the function on_enter upon entering my text and pressing down the enter button. However, then I get the error message:
TypeError: on_enter() missing 1 required positional argument: 'secondvalue'
If I change the function on_enter to accept 2 arguments,
def on_enter(instance, secondvalue):
print(secondvalue)
This now prints <kivy.uix.textinput.TextInput object at 0x0000000003A432B8>, but doesn't recover the text.
I'm confused about what Kivy is doing at their backend and I can't find any answers in their documentations. Why is it that on_enter accepts 2 arguments while on_text 3?
Bind connects an event with a function.
In your case, the first event is the on_text_validate of the TextInput widget (the event that is emitted when you press Enter while on its text field), and the second is text (when the text of the field is changed).
These events trigger their dedicated functions using different arguments.
Both of them send as first argument the widget that produce them (the TextInput instance).
The text also sends the changed text.
To get the text of the on_text_validate event, you can get the TextInput text property like this:
print(instance.text)
Related
I've got a problem tying callback funtions to the on_focus event of a TextInput.
I want it to trigger a validation event when the focus from the input widget is removed. And, in doing so, calling another method (via the on_validate_text method)
Here is the code:
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.app import App
class MyTextInput(TextInput):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.multiline = False
self.unfocus_on_touch = True
def on_focus(self, instance, value):
if not value: # DEFOCUSED
print('Focus is off')
class MainLayout(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
#First row
self.top_label = Label(text = 'No text')
self.add_widget(self.top_label)
self.top_input = MyTextInput(on_text_validate=self.change_top_label)
#Here im trying to trigger the validate event when the on_focus gets called
self.top_input.bind(on_focus=self.top_input.on_text_validate)
self.add_widget(self.top_input)
#Second row
self.bottom_label = Label(text='Bottom Label')
self.add_widget(self.bottom_label)
self.bottom_input = MyTextInput(on_text_validate=self.create_popup)
self.bottom_input.bind(on_focus=self.bottom_input.on_text_validate)
self.add_widget(self.bottom_input)
def change_top_label(self, instance):
self.top_label.text = instance.text
instance.text = ''
def create_popup(self, instance):
self.my_popup = Popup(title=instance.text, size_hint=(.5, .5))
self.my_popup.content = Button(text='CLOSE', on_release=self.my_popup.dismiss)
self.my_popup.open()
instance.text = ''
if __name__ == '__main__':
class MainApp(App):
def build(self):
return MainLayout()
MainApp().run()
In this case, when de top input gets defocused, I want it to call the change_top_label method through the validation event.
In the same way, when the bottom input gets defocused, the create_popup method should get called through the validation event.
I need both input to call a callback function when unfocused. But I can not define that function inside the on_focus method, because it needs to be different for every instance of MyTextInput.
I've tried binding on_text_validate to on_focus, and calling on_text_validate inside the on_focus metyhod, but it does not work.
Clearly there is something I'm missing.
If you could help me out here, It'd be great.
First of all, your code seems already doing what you wanted in the following (after the method on_text_validate is being called),
In this case, when de top input gets defocused, I want it to call the change_top_label method through the validation event...
Secondly,
I've tried binding on_text_validate to on_focus...
This seems confusing to me. The method on_text_validate gets called when you hit 'enter' (and if multiline is set to False) and that will also unfocus the TextInput. Also on_focus is kind of a default method that is called whenever the attr. focus changes. So and finally if you want just this,
I need both input to call a callback function when unfocused. But...
You can do that as TextInput_instance.bind(focus = some_method).
This question already has answers here:
clicked.connect() Error
(3 answers)
Closed 1 year ago.
I'm new to Python 3.8 and PyQT5 and I'm trying to make an application with a GUI.
I've created a login form with two QLineEdits (User and Password). I want to check if what the user entered as User/Password is in a database using a function that it's in other file once the user clicks the 'Login' button. The class I've created for the form is similar to the next one:
# Where the check function for the password is
from CLBK_CheckPassword import CheckPassword
from PyQt5 import QtWidgets
class W_Password(QtWidgets.QWidget):
def __init__(self, App):
super(W_Password,self).__init__()
# Set the window title and size
WindowHeight = 400
WindowWeight = WindowHeight/3
self.setWindowTitle("Login Window")
self.resize(WindowHeight,WindowWeight)
# Text box
self.Input_User = QtWidgets.QLineEdit()
self.Input_Password = QtWidgets.QLineEdit()
self.Input_Password.setEchoMode(QtWidgets.QLineEdit.Password)
# Button
self.Button_Login = QtWidgets.QPushButton("Login")
self.Button_Login.clicked.connect(CheckPassword())
# Layout and items positioning
self.Layout = QtWidgets.QGridLayout(self)
self.Layout.addWidget(self.Button_Login,3,2,1,2)
self.Layout.addWidget(self.Label_User,0,0,1,1)
self.Layout.addWidget(self.Input_User,0,1,1,3)
self.Layout.addWidget(self.Label_Password,1,0,1,1)
self.Layout.addWidget(self.Input_Password,1,1,1,3)
I have two problems with this code:
When the form is created and shown, the function 'CheckPassword()' is automatically executed (it should be only executed if the user press the button).
The second problem is that I've the following error once the function has been executed : TypeError: argument 1 has unexpected type 'NoneType'
Problem 1
You should pass the CheckPassword function as a parameter to event listener clicked.connect.
So
self.Button_Login.clicked.connect(CheckPassword())
line changes to:
self.Button_Login.clicked.connect(CheckPassword)
Problem 2
Your function CheckPassword seems to takes at least one parameter. So you have to change logic a little bit.
You can use lambda expression:
self.Button_Login.clicked.connect(lambda: (CheckPassword(THE_PARAMETER)))
Edit
Answer to: what if the function 'CheckPassword' returns something?
As musicamante mentioned you can have another method which would run your function and capture the return value like:
# Where the check function for the password is
from CLBK_CheckPassword import CheckPassword
from PyQt5 import QtWidgets
class W_Password(QtWidgets.QWidget):
def __init__(self, App):
super(W_Password,self).__init__()
...
self.Button_Login.clicked.connect(lambda: (self.checher()))
...
def checher(self):
value = CheckPassword(THE_PARAMETER)
# Do something with value
It's my code. How to store the lastly clicked/checked button as default in the next opening of the programme? For example: If we run the programme and click the 3rd button and close the entire programme. Whenever I reopen/re-run the programme, the lastly clicked button is checked by default ( ie. third button is checked by default)
import sys
from PyQt5 import QtWidgets
class RadioButton(QtWidgets.QWidget):
def __init__(self):
super(). __init__()
self.setWindowTitle("Radio Button")
self.rbtn1 = QtWidgets.QRadioButton("Button1")
self.rbtn1.clicked.connect(self.getvalue)
self.rbtn2 = QtWidgets.QRadioButton("Button2")
self.rbtn2.clicked.connect(self.getvalue)
self.rbtn3 = QtWidgets.QRadioButton("Button3")
self.rbtn3.clicked.connect(self.getvalue)
self.rbtn4 = QtWidgets.QRadioButton("Button4")
self.rbtn4.clicked.connect(self.getvalue)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(self.rbtn1)
vbox.addWidget(self.rbtn2)
vbox.addWidget(self.rbtn3)
vbox.addWidget(self.rbtn4)
self.setLayout(vbox)
def getvalue(self):
if self.rbtn1.isChecked():
self.rbtn1.setChecked(True)
print("Radio button 1 is checked")
elif self.rbtn2.isChecked():
self.rbtn2.setChecked(True)
print("Radio button 2 is checked")
elif self.rbtn3.isChecked():
self.rbtn3.setChecked(True)
print("Radio button 3 is checked")
elif self.rbtn4.isChecked():
self.rbtn4.setChecked(True)
print("Radio button 4 is checked")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
win = RadioButton()
win.show()
sys.exit(app.exec_())
You can use QSettings to save and restore data. Note that values are serialized as QVariants, so you cannot, for instance, save a custom python subclass instance.
In order to properly use QSettings you must set both the Qt application name and organization name (otherwise data won't be stored anywhere).
It is possible to save settings in other ways, but for general usage it's better to use the default behavior.
Note that, since values are stored as strings, Qt can only try to guess the actual type, so it's always better to specify the type when using value() (see the case below).
Then, while by default Qt is able to automatically group radio buttons and make them exclusive (see the autoExclusive property of QAbstractButton, which is set to True for QRadioButtons), for this kind of situations it's better to rely on a QButtonGroup, which not only allows better control on the buttons, but also allows identifying buttons member of the group by using unique IDs they were assigned to.
Finally, it's just a matter of connecting the appropriate signal of the button group to the function that saves the currently checked button.
from PyQt5 import QtWidgets, QtCore
class RadioButton(QtWidgets.QWidget):
def __init__(self):
super(). __init__()
self.setWindowTitle("Radio Button")
self.rbtn1 = QtWidgets.QRadioButton("Button1")
self.rbtn2 = QtWidgets.QRadioButton("Button2")
self.rbtn3 = QtWidgets.QRadioButton("Button3")
self.rbtn4 = QtWidgets.QRadioButton("Button4")
buttons = self.rbtn1, self.rbtn2, self.rbtn3, self.rbtn4
vbox = QtWidgets.QVBoxLayout(self)
self.optionGroup = QtWidgets.QButtonGroup()
for i, button in enumerate(buttons):
vbox.addWidget(button)
self.optionGroup.addButton(button, i)
# for Qt>=5.15:
# self.optionGroup.idToggled.connect(self.getvalue)
# otherwise:
self.optionGroup.buttonToggled[int, bool].connect(self.getvalue)
self.settings = QtCore.QSettings()
# read the value if it exists, otherwise use a default; note that
# the type MUST be specified, otherwise numbers would be read as
# strings, raising an exception and crashing the program in this case
default = self.settings.value('MyOption', 2, type=int)
self.optionGroup.button(default).setChecked(True)
def getvalue(self, id, checked):
if checked:
button = self.optionGroup.button(id)
print("{} is checked".format(button.text()))
self.settings.setValue('MyOption', id)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
app.setOrganizationName('MyOrganization')
app.setApplicationName('MyApplication')
win = RadioButton()
win.show()
sys.exit(app.exec_())
I strongly recommend you to carefully study the whole documentation about all the classes specified above.
I am developing a kivy app in which ,there are two screens
1.LoginScreen
and 2.HomeScreen.
What required is -
A value 'xyz' which is computed in class LoginScreen, to be passed to the method 'insertdata' of class HomeScreen and want to display that value on a label.
For this I tried following code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager
class loginScreen(Screen):
def __init__(self,**kwargs):
super(HomeScreen, self).__init__(**kwargs)
def auth(self):
xyz=1
self.manager.current="home"
obj=HomeScreen()
# 1. To Pass 'xyz' to method scrn2
HomeScreen.insertdata(obj)
class HomeScreen(Screen):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
if (a==1):
# 2. To display label
self.scrn2()
def insertdata(self):
print "screen 2"
label=Label(text="good moring")
self.add_widget(label)
class ScreenApp(App):
pass
if __name__ == '__main__':
ScreenApp().run()
Here:
insertdata is called from method auth
1)the 1st way is proper , as it is passing 'xyz' and calling the method insertdata but it dosen't display label
2) in 1st way I have to create to create an object of HomeScreen ,to call insertdata, which in turn calls the ___init__ of Homescreen and init calls insertdata
insertdata called from __init__
1) It loads data before user authentication at loginscreen
insertdata gets total 3 calls, which is affecting app launch time.
Suggest me any simple and effective way for this.
Thanks in advance.
You can use Screen.manager() method to get an manager object from one screen, use it to retrieve another one with its ScreenManager.get_screen() method and then pass the value directly. For an working example check an answer of this question: Kivy - Slider Class value change on another screen
I've got a small PyGTK program, that has a statusicon. Upon left click on the statusicon a window with a TextView should appear and a predefined text should be shown in the TextView widget. My problem is that I don't know how to pass the text to shown as a parameter to the method that creates the window. I can create the window with a TextView without problems, but I cannot insert text into it.
Here's my code:
import gtk
import keybinder
class PyPPrinter(object):
def __init__(self):
self.staticon = gtk.StatusIcon()
self.staticon.set_from_stock(gtk.STOCK_INDEX)
self.staticon.set_visible(True)
self.staticon.connect('activate', self.browser(output_text = 'text'))
gtk.main()
def browser(self, window, output_text):
browser = gtk.Window()
browser.set_usize(600, 500)
textbox = gtk.TextView()
text = gtk.TextBuffer()
text.set_text(output_text)
textbox.set_buffer(text)
browser.add(textbox)
browser.show_all()
if __name__ == '__main__':
PyPPrinter()
This code gives me an exception: TypeError: browser() takes exactly 3 arguments (2 given). Perhaps I should also pass a value for the window parameter, but what should it be?
Two variants:
Change connect part:
self.staticon.connect('activate', self.browser(output_text = 'text'))
to:
self.staticon.connect('activate', self.browser, 'text')
or change Handler-Signature:
def browser(self, window, output_text):
to:
def browser(self, window):