Reputation: 1147
G'day!
I'm developing a Python GUI using PySide. One of the features I'm trying to get functional is the ability to count the number of login attempts from a user of the system. This is very simple for non-GUI applications using the input() command, but I'm having some trouble getting my head around the self.lineEdit.returnPressed.connect() command.
As you can see below, I've got a While statement that allows up to 3 login attempts. However, if I try and use the method I normally use for monitoring keyboard input, such as:
n = 1
while n < 4:
user_barcode = input()
if user_barcode == 'James':
print("Welcome James!")
else:
print("User not recognised, please try again.")
n = n + 1
print("Sorry, you are not a recognised user of this system.")
exit
However, now that I'm basing the user_barcode off a lineEdit in the GUI, the above approach doesn't work. If I replace input() with self.lineEditScanBarcode.text(), the while statement automatically iterates 3 times with only 1 input into the lineEdit. Ie. I type a user name, hit enter, and it automatically iterates 3 times. How do I 'ask' for input into the lineEdit before it commences each iteration?
I'm guessing I have to use the signal/slot ideology, however my understanding is that I would have to then direct the lineEdit input to a separate function using the self.lineEditScanBarcode.returnPressed.connect() action. I've had a go at it below, but it's a trainwreck!
def Login(self):
global user_barcode
user_barcode = self.lineEditScanBarcode.text()
i = 1
while i < 4:
print(i)
self.lineEditScanBarcode.returnPressed.connect(LoginAttempt)
def LoginAttempt(self):
with open('Users.csv', 'rt') as f:
reader = csv.reader(f)
for row in reader:
for field in row:
if field == user_barcode:
global row_number
row_number = n
self.LoggedIn()
if user_barcode == 'Calibration':
self.lineEditScanBarcode.clear()
showCalibrationCertificate.show()
else:
if user_barcode not in open('Users.csv', 'r').read():
print("Unauthorised access request.")
i = i + 1
self.lineEditScanBarcode.clear()
self.LCDLoginAttempt.display(i-1)
next
print("Sorry, you are not an authorised user of this system. Please contact the system administrator for further clarification, or if you feel this is an error.")
Is there some obvious trick I'm missing here? I'm hoping that there is a way to use the returnPressed action, such that it behaves like the input() action. That way, I can have the returnPressed action at the start of the iteration, and the script waits for the enter key to be pressed on the lineEdit before continuing.
Anyone have any ideas?
Thanks!
Upvotes: 0
Views: 346
Reputation: 365797
You're missing the most fundamental part of (PySide-style) GUI programming: Event loops.
The GUI runs a main event loop. Whenever it finds an event that you registered a handler for, it calls your handler.
Your handler function has to return as soon as possible. It can't sit around waiting for user input, or nobody is processing any events while it's waiting. That means nobody is ever going to fire your next connect
, because there's nobody to do it. It also means that nobody is going to redraw the screen or respond to the OS checking whether your app is alive or anything else.
This often means you need to turn your logic "inside out". Something like this:
def Login(self):
global user_barcode
self.login_attempts = 1
self.lineEditScanBarcode.returnPressed.connect(LoginAttempt)
def LoginAttempt(self):
user_barcode = self.lineEditScanBarcode.text()
# all the stuff that validates the barcode
if valid:
self.LoggedIn()
return
print("Unauthorised access request.")
self.login_attempts += 1
self.lineEditScanBarcode.clear()
self.LCDLoginAttempt.display(self.login_attempts-1)
if self.login_attempts == 4:
print("Sorry, you are not an authorised user of this system. Please contact the system administrator for further clarification, or if you feel this is an error.")
# probably want to do something here... disconnect the signal? quit?
Of course a print
statement isn't likely to be very useful in a GUI app. And various other things seem suspicious.
But this is the basic idea. You don't loop, waiting for the user to make four attempts; you register for user attempts, and keep a counter that's shared across all of the calls to your function.
Upvotes: 2