Tkinter- Exit without saving- Warning dialogue box or askquestion - python

Exit without Saving
How should I program my exit function that if user exit without saving then it should pop a question
def main(self):
...
file.add_command(label="New",command=lambda: self.new())
file.add_command(label="Open",command=lambda: self.load())
file.add_command(label="Save",command=lambda: self.save())
file.add_command(label="Exit",command=self.exit)
menu.add_cascade(label="File",menu=file)
def exit(self):
result = askquestion("Exit", "Are You Sure Without Saving?", icon='warning')
if result == "yes":
exit()
else:
return False

Have a global variable called
hasBeenSaved = False
When you call the save() function, switch that global boolean hasBeenSaved to True.
If the user does anything else to change their file, set hasBeenSaved back to False .
Now, when you are going through your exit() function, if hasBeenSaved is False, prompt the user for:
"Are you sure you wanna exit without saving?"
Hope this helps!

Related

Python click command exit flow

I have this python click CLI design (wificli.py). At the end of command execution, it prints only respective print messages.
For example, when executed command python3.7 wificli.py png, it prints only png and when executed command python3.7 wificli.py terminal it prints only terminal.
As I have shown, I am expecting that it would also print End of start function and End of the main function but it is not. The idea is that to do clean up of resources only at one place rather than at each exit point of the respective command.
import click
#click.group()
#click.option('--ssid', help='WiFi network name.')
#click.option('--security', type=click.Choice(['WEP', 'WPA', '']))
#click.option('--password', help='WiFi password.')
#click.pass_context
def main(ctx, ssid: str, security: str = '', password: str = ''):
ctx.obj['ssid'] = ssid
ctx.obj['security'] = security
ctx.obj['password'] = password
#main.command()
#click.pass_context
def terminal(ctx):
print('terminal')
#main.command()
#click.option('--filename', help='full path to the png file')
#click.pass_context
def png(ctx, filename, scale: int = 10):
print('png')
def start():
main(obj={})
print('End of start function')
if __name__ == '__main__':
start()
print('End of main function')
When executed
As you've not asked a specific question, I can only post what worked for me with the reasoning behind it, and if this is not what you are looking for, I apologize in advance.
#main.resultcallback()
def process_result(result, **kwargs):
print('End of start function')
click.get_current_context().obj['callback']()
def start():
main(obj={'callback': lambda: print('End of main function')})
So, the resultcallback seems to be the suggested way of handling the termination of the group, and the invoked command. In our case, it prints End of start function, because at that point, the start function has finished executing, so we are wrapping up before terminating main. Then, it retrieves the callback passed in via the context, and executes that.
I am not sure if this is the idiomatic way of doing it, but it seems to have the intended behaviour.
For the result callback, a similar question was answered here
As to what exactly is causing this behaviour, and this is only a guess based on some quick experimentation with placing yield in the group or the command, I suspect some kind of thread/processor is spawned to handle the execution of the group and its command.
Hope this helps!
click's main() always raises a SystemExit. Quoting the documentation:
This will always terminate the application after a call. If this is not wanted, SystemExit needs to be caught.
In your example, change start() to:
def start():
try:
main(obj={})
except SystemExit as err:
# re-raise unless main() finished without an error
if err.code:
raise
print('End of start function')
See the click docs here also this answer here
# Main Runtime call at bottom of your code
start(standalone_mode=False)
# or e.g
main(standalone_mode=False)

Self made exception shows up way later when exit() is called. Again

Here are the parts of my code that could have something to do with the problem: (I cut away as much as possible)
import os
import getpass
def PAUSE():
input("= Press <ENTER> to continue...")
def clearscreen():
os.system('cls' if os.name=='nt' else 'clear')
def loginscreen():
clearscreen()
print("==================================================================")
print("= LOGIN =")
print("==================================================================")
print("= None Important. =")
print("==================================================================")
username = input("= Please enter your username: ")
password = getpass.getpass("= Please enter the password that belongs to that username: ")
print("==================================================================")
try:
# I had too cut away the MariaDB Section for a MCVE, and thus i had to fill the dbusername and sdbpassword and isadmin, but without modifying relevant code. Thus i might have made a mistake in this array, dont use them alot sooo... if this were to occur i am sorry....
['dbusername = "stackoverflow", dbpassword = "stackoverflow", isadmin = "No"']
for row in results:
dbusername = row[0]
dbpassword = row[1]
isadmin = row [2]
if username == dbusername:
if password == dbpassword:
if isadmin == "Yes":
admin_main_menu()
elif isadmin == "No":
clearscreen()
main_menu()
########## For some reason the same problem arises when i use the commented away code under this comment.
# clearscreen()
# print("==============================================")
# print("= Unkown Username / Password =")
# print("==============================================")
# PAUSE()
# print("==============================================")
# loginscreen()
except:
clearscreen()
print("Failed to check codes. (Error: 5646FCJU), Contact N.S. Geldorp")
PAUSE()
def main_menu():
clearscreen()
print("=============================================")
print("= Main Menu =")
print("=============================================")
print("= 1. All unimportant... =")
print("= 5. Exit =")
print("=============================================")
answer = input("= Please enter the number of the function you wish to use: ")
print("=============================================")
clearscreen()
if answer == "1":
# print_animals()
print("Not needed")
PAUSE()
elif answer == "5":
# pass
print("Exiting...")
exit()
else:
print("Unimportant...")
PAUSE()
main_menu()
Now, I cut away everything but perhaps relevant parts of the login screen and the standard main menu. And of course the functions as PAUSE and clearscreen as they always reappear in relevant functions. At least if I wrote them. Now what happens is that when I have a successful login and I go to the menu, And I decide to exit, It shows me the error written in the except of the login screen... I don't get it, do you?
This is demonstration 1,442,633 of why you must never, ever use a blank except clause.
sys.exit() works by raising an exception: SystemExit. Normally, that exception bubbles all the way up to the interpreter, which catches it and exits gracefully. But because your try/except code catches everything, it catches that too; so you see your own error message instead of the interpreter quitting.
You should only ever catch the things that you know you can deal with. I'm not sure what exceptions you are expecting with that code, but presumably they are ones that are raised by the database code. You should work out which ones could be raised, and catch those only: for example, except TypeError:.
At the very least, you should restrict your except to only catch actual errors, which you can do with except Exception:; SystemExit descends from BaseException, which is the parent class of Exception which all other runtime errors descend from. But, you really shouldn't do that, you should catch the specific exceptions only.
(Note also, it makes no sense to have that for loop over the database results; I don't understand why you have done that.)

What's the best way to use a QMessageBox and return back to main form after clicking OK in PyQt?

I'm using a QMessageBox to tell the user if a field they entered is incorrect or missing before submitting the main form which triggers the run. Currently when the QMessageBox pops up, the main window disappears (I thought it would stay behind it but modal) and when you click OK, the whole application closes. I've looked at examples, but I can't tell what I'm doing wrong. Could someone please help?
Here's this piece of the code:
def isComplete(self):
complete = True
# check field
variable = self.dlg.ui.txtField.text()
if variable:
# got a non-empty string
else:
complete = False
msgBox = QtGui.QMessageBox()
msgBox.setText("Please fill in all required fields")
msgBox.exec_()
return complete
def run(self):
# show dialog
self.dlg.show()
# run the dialog event loop
result = self.dlg.exec_()
# check necessary fields
complete = self.isComplete()
# see if OK was pressed and fields are complete
if (result and complete):
self.runCalcs()
In simple cases you can use static methods information, question, warning and critical of QMessageBox. It will be modal if parent arg is specified:
def isComplete(self):
complete = True
# check field
variable = self.dlg.ui.txtField.text()
if variable:
# got a non-empty string
else:
complete = False
QtGui.QMessageBox.warning(self, "Warning", "Please fill in all required fields")
return complete
def run(self):
# show dialog
self.dlg.show()
# run the dialog event loop
result = self.dlg.exec_()
# check necessary fields
complete = self.isComplete()
# see if OK was pressed and fields are complete
if (result and complete):
self.runCalcs()

Python global var in If statement doesn't work

First, I am an absolute beginner and sorry if I ask stupid questions.
I try to code a little program for school.
Imagine a motor with three emergency switches. A "Overheating", a "Circuit breaker" and a "manual"-switch witch all stop the motor.
In the program, the switches are simulated by tkinter-buttons in a little GUI.
If you press the button, it should output whatever case is simulated.
If the motor "stopped" but a button (or a new button) is pressed again, a message "Machine already stopped" should appear.
But that last part of the program does not work.
I've learned that vars in Python are local by default and so I tried to define the var "triggered" as global. But I've probably made some mistakes.
If I run the program, the first message (for example "Overheating!") appears but the second message "Machine already stopped" is missing when the button is pressed again.
Can you tell me where my fault is? I tried to google it but I don't know what is wrong. Sometimes it is difficult to read threads or tutorials because I am not native english-speaking.
And please tell me if there's any pseudocode in there.
As I said I am an absolute beginner but I try hard to learn it.
from tkinter import *
import sys, os
root = Tk()
root.title("Control Panel")
root.geometry("400x200")
app = Frame(root)
app.grid()
# Vars can be used later
overheat = False
# Stops motor if temperature is too high
circuitbreaker = False
# Stops if current flow is too high
manual = False
# Stops when switch is triggered manually
global triggered
triggered = False
# Returns True if one emergency unit has triggered
def Button_Overheat():
global triggered
if triggered == False:
triggered = True
print("Overheating!")
blockPrint()
else:
enablePrint()
print("Machine already stopped")
blockPrint
return
button_overheat = Button(app, text = "Overheat", command = Button_Overheat)
button_overheat.grid()
def Button_CircuitBreaker():
global triggered
if triggered == False:
print("Overload! Breaking Circuit...")
blockPrint()
else:
print("Machine already stopped")
blockPrint()
return
button_cicuitbreaker = Button(app, text = "Circuitbreaker", command = Button_CircuitBreaker)
button_cicuitbreaker.grid()
def Button_Manual():
global triggered
if triggered == False:
print("Machine was manually stopped")
blockPrint()
triggered = True
else:
print("Machine already stopped")
blockPrint()
return
button_manual = Button(app, text = "Turn off manually", command = Button_Manual)
button_manual.grid()
def blockPrint():
sys.stdout = open(os.devnull, 'w')
def enablePrint():
sys.stdout = sys.__stdout__
mainloop()
Please notice that other than in Overheating you never re enabled printing to allow it to print "Machine already stopped".
Just add enablePrint() to the other two options else clauses as well:
def Button_CircuitBreaker():
global triggered
if triggered == False:
print("Overload! Breaking Circuit...")
blockPrint()
else:
enablePrint()
print("Machine already stopped")
blockPrint()
return
def Button_Manual():
global triggered
if triggered == False:
print("Machine was manually stopped")
blockPrint()
triggered = True
else:
enablePrint()
print("Machine already stopped")
blockPrint()
return

Run a logging filter in a separate thread - Python

I have a logging filter that checks for an environment variable to change and I want it to run (in the background) in a thread separate from the process that is setting the environment variable.
What I'm trying to do: every time logging.ERROR is called in my code, the user is alerted to the error and prompted on whether or not they want to continue. Separately the filter and the prompt work correctly however, when I put them together I have a problem. I need to have the filter running in the background so the code to prompt the user can run simultaneously (right now, the filter executes first and the prompt shows up after the while loop in the filter times out, at which point it is useless).
My filter code:
class ErrorFilter(logging.Filter):
def __init__(self,level):
self.level = level
thread = threading.Thread(target=self.filter,args=())
thread.daemon = True
thread.start()
def filter(self,record):
if record.levelno == self.level:
os.environ["ERROR_FLAG"] = "True"
timeout = time.time() + 60*1 #set the timeout to 1 minute
while True:
print "waiting..."
keep_going = os.environ.get("CONTINUE_FLAG")
#wait for user to respond
if keep_going == "False" or time.time() > timeout:
print "oops there's a problem, quitting."
break
if keep_going == "True":
print "Continuing"
break
os.environ["CONTINUE_FLAG"] = "None"
I have another short method that "listens" for ERROR_FLAG and then asks for input using:
def continueAsk(message, title="Warning! Continue?", yn=("Yes","No")):
yes = set(['yes','y', 'ye', '', 'canhaz'])
no = set(['no','n', 'lolzno'])
tryLimit = 0
while tryLimit < 100:
sys.stdout.write(message + ": ")
choice = raw_input().lower()
if choice in yes:
return True
elif choice in no:
return False
else:
tryLimit+=1
sys.stdout.write("Please respond with 'yes' or 'no'.")
EDIT
I've also tried using multiprocessing in my filter like this:
from multiprocessing import Process, Queue
def __init__(self,level):
self.level = level
queue = Queue()
p = Process(target=self.filter,args=("hi"))
p.start()
p.join()
I've tried setting up my filter so it runs in a different thread, but I've not had any luck so far (the filter still runs first, followed by the prompt) and I've never used multithreading before. I know this is not a traditional use of the logger, but I appreciate any input on this.
Looking at the subprocess and multiprocess documentation, I think one of those might work as well but am not sure.

Categories

Resources