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
Related
I'm trying to create a script that makes API calls to a website to get info (via the requests module).
The user can then manipulate the website using my script/gui.
My app's main window will have several buttons at the bottom which acts like tabs to switch between windows(let's say for argument's sake 2 buttons to switch between two windows).
When certain changes are made, I need the QStackedWidget to refresh all the widgets/labels in certain windows that are not currently being displayed.
Summary of my code:
# Global Variables
app = QApplication(sys.argv)
win = QtWidgets.QStackedWidget()
response = {} # global dict to store API response from website
class UiWindowOne(QMainWindow):
# This window mostly shows information that I get from the website.
def __init__(self):
super(UiWindowOne, self).__init__()
self.setup_ui(self)
self.retranslate_ui(self)
# Then I map buttons to methods
def setup_ui(self, WindowOne):
# This was generated by QT Designer and places widgets
def retranslate_ui(self, WindowOne):
# This was generated by QT Designer and places widgets
def refresh(self):
'''
This function refreshes the current window. Basically, I put everything in the __init__ function in here (except "super(UiWindowOne, self).__init__()".
:return: None
'''
self.setup_ui(self)
self.retranslate_ui(self)
# Also map buttons to methods
class UiWindowTwo(QMainWindow):
def __init__(self):
super(UiWindowTwo, self).__init__()
self.setup_ui(self)
self.retranslate_ui(self)
# Then I map buttons to methods
def setup_ui(self, WindowTwo):
# This was generated by QT Designer
def retranslate_ui(self, WindowTwo):
# This was generated by QT Designer
def refresh(self):
'''
This function refreshes the current window. Basically, I put everything in the __init__ function in here (except "super(UiWindowTwo, self).__init__()".
:return: None
'''
self.setup_ui(self)
self.retranslate_ui(self)
# Also map buttons to methods
def update_website(self):
# Make changes to website
# After changes were made, I want to get modified info from the website and re-initialize/refresh both windows to reflect the changes made.
# I can easily call self.refresh() to refresh WindowTwo. But I cannot refresh WindowOne from here.
def main():
# Here I make API calls to the Website to get info/images
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(".\\imgs/static/A.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
win.setWindowIcon(icon)
win.setWindowTitle("NAME")
first_window = UiWindowOne()
second_window = UiWindowTwo()
win.addWidget(first_window)
win.addWidget(second_window)
win.setGeometry(250, 250, 820, 854)
win.setFixedWidth(820)
win.setFixedHeight(854)
win.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
I have tried doing a "first_window.refresh()" under the update_website() function in UiWindowTwo, but then python tells me that first_window is not defined.
I then tried making first_window and second_window global variables, but then I ended up reordering my whole script and couldn't get it to run.
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)
I try to validate if a editable QCombobox input is a directory or not before it gets added to the QCombobox.
from PySide import QtGui, QtCore
class DirValidator(QtGui.QValidator):
def __init__(self, cb_input):
super(DirValidator, self).__init__()
self._input = cb_input
def validate(self, _text, _pos):
_dir = QtCore.QDir(_text)
if self._input.hasFocus(): # ignore validation while editing not complete
return QtGui.QValidator.Acceptable
if QtCore.QDir.exists(_dir):
return QtGui.QValidator.Acceptable
return QtGui.QValidator.Invalid
dir_validator = DirValidator(self.cb_path.lineEdit())
self.cb_path.setValidator(dir_validator)
sadly it does not work properly because every input still gets added to the combobox when i hit enter.
Any suggestions?
EDIT: i also tried to use the validator on the QLineEdit like so:
dir_validator = DirValidator(self.cb_path.lineEdit())
self.cb_path.lineEdit().setValidator(dir_validator)
Does not work either.
EDIT: It kinda works...but when i press return "hasFocus" is still True so it is just accepting the input and then of course adding it to the combobox. if i get rid of "if self._input.hasFocus():" it does not accept any input if i type it in...just if a paste a complete directory path.
So what i need is a way to check if the edit is finished to then check if it is a directory.
And as far as i understand i can only check this in a combobox via QValidator...because it adds the input to the box right away...before i can intercept it in any way.
EDIT: i did find solution for my case. I just abandoned the whole validator approach. The purpose of that was to prevent the combobox from creating a new item if it was no valid directory...what i now did instead was to validate the input after it was finished by taking advantage of the QLineEdit().editingFinished() signal. After it created the new Item i just removed it again if the input was not valid and it also gave me the opportunity to add a error Popup telling the user that the input was no directory.
I do not see the need for hasFocus(), because if you are writing to the QLineEdit it obviously has the focus. If the path is incorrect then you must return a QValidator::Intermediate:
from PySide import QtGui, QtCore
class DirValidator(QtGui.QValidator):
def validate(self, _text, _pos):
_dir = QtCore.QDir(_text)
if _dir.exists():
return QtGui.QValidator.Acceptable
return QtGui.QValidator.Intermediate
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
combo = QtGui.QComboBox(editable=True)
dir_validator = DirValidator(combo.lineEdit())
combo.setValidator(dir_validator)
combo.show()
sys.exit(app.exec_())
I have two buttons (Eg. A andB) which do the same things (based on user selection). So when you select something, then click the button, the selection's name will be input into the line-edit for the button. For example, if I click on buttonA, the input will be to lineEditA.
Currently I have created a signal function as follows:
def _connections_setup(self):
self.btnA.clicked.connect(self.get_sel_nameA)
self.btnB.clicked.connect(self.get_sel_nameB)
def get_sel_nameA(self):
sel_name = get_name_from_sel()
self.line_editA.setText(sel_name)
def get_sel_nameB(self):
sel_name = get_name_from_sel()
self.line_editA.setText(sel_name)
"""
def get_sel_name(self):
# Returns me a blank
button = self.sender()
print button.objectName()
# My objective here would be, if btnA is clicked, the sel_name will be inputted into lineEditA. Likewise for btnB
"""
Instead of creating two similar functions, how can I determine which button was clicked and have the selection's name to be input correctly into the line-edit?
I tried using self.sender() (see get_sel_name()) but it does not seems to return me the button name.
The sender() function only works in slots directly connected to signals. So your code needs to look something like this:
def _connections_setup(self):
self.btnA.clicked.connect(self.get_sel_name)
self.btnB.clicked.connect(self.get_sel_name)
def get_sel_name(self):
button = self.sender()
name = button.objectName()
if button is self.btnA:
self.line_editA.setText(name)
elif button is self.btnB:
self.line_editB.setText(name)
is there any way to have an input box inside of an message box opened with the ctypes library? so far I have:
import ctypes
messageBox = ctypes.windll.user32.MessageBoxA
title = 'Title'
text = 'Message box!'
returnValue = messageBox(None, text, title, 0x40 | 0x1)
print returnValue
and this gives a message box with an image icon and two buttons, both of which I know how to change, and it sets a variable "returnValue" to a number representing the button clicked. However, I also need a variable that will set to a string input in the message box. The reason I need this and I can't just do simple a = raw_input('prompt') is that I want the program itself to run in the background (it would launch itself on logon).
If you want a simple solution, use the PyMsgBox module. It uses Python's built-in tkinter library to create message boxes, including ones that let the user type a response. Install it with pip install pymsgbox.
The documentation is here: https://pymsgbox.readthedocs.org/
The code you want is:
>>> import pymsgbox
>>> returnValue = pymsgbox.prompt('Message box!', 'Title')
Message box is for messages only. What you need is QDialog. You can create it in QtDesigner(I have login dialog created this way, with 2 QLineEdit for username and pass, 2 buttons in QDialogButtonBox and QCombobox for language choose). You'll get .ui file, which you'll need to convert into .py this way in cmd:
pyuic4 -x YourLoginDialogWindow.ui -o YourLoginDialogWindow.py
import created YourLoginDialogWindow.py and you can use it and implement any method you need:
import YourLoginDialogWindow
class YourLoginDialog(QtGui.QDialog):
def __init__(self, parent = None):
super(YourLoginDialog, self).__init__(parent)
self.__ui = YourLoginDialogWindow.Ui_Dialog()
self.__ui.setupUi(self)
...
self.__ui.buttonBox.accepted.connect(self.CheckUserCredentials)
self.__ui.buttonBox.rejected.connect(self.reject)
def GetUsername(self):
return self.__ui.usernameLineEdit.text()
def GetUserPass(self):
return self.__ui.passwordLineEdit.text()
def CheckUserCredentials(self):
#check if user and pass are ok here
#you can use self.GetUsername() and self.GetUserPass() to get them
if THEY_ARE_OK :
self.accept()# this will close dialog and open YourMainProgram in main
else:# message box to inform user that username or password are incorect
QtGui.QMessageBox.about(self,'MESSAGE_APPLICATION_TITLE_STR', 'MESSAGE_WRONG_USERNAM_OR_PASSWORD_STR')
in your __main__ first create login dialog and then your main window...
if __name__ == "__main__":
qtApp = QtGui.QApplication(sys.argv)
loginDlg = YourLoginDialog.YourLoginDialog()
if (not loginDlg.exec_()):
sys.exit(-1)
theApp = YourMainProgram.YourMainProgram( loginDlg.GetUsername(), loginDlg.GetPassword())
qtApp.setActiveWindow(theApp)
theApp.show()
sys.exit(qtApp.exec_())