How to get a menu checkbutton to call a function in Python + Tkinter? - python

so, I'm now in a personal project (just to try myself) with python + tkinter. It is a encrypter, which means that it gets a piece of text and encrypts it using some famous cyphers (like the numerical cypher, the caesar's cypher, and others).
Now, I wanted to give the user the option to save the text he encrypted, and also the encrypted text generated by the program. For that, I created two checkbutton on the program's menu: one for "save text" and the other for "save encrypted text". My question is, I tried to attach a function as it's command option, so, I guess it should run that function when the option is clicked. But it isn't happening.
I'll just explain what the funcitions shouls do before passing the code.
They should prompt a question, asking the user if he/she really wants to create a text file with the text and the encrypted text (This isn't a database, it's just something for the user to be able to read later the text's he/she encrypted and the encrypted version, if he/she wants to).
So, to the code:
encryptermenu = Menu(menubar, tearoff=0)
encryptermenu.add_checkbutton(label="Save Text", variable=v, command=saveText)
encryptermenu.add_checkbutton(label="Save Encrypted Text", variable=w, command=saveEncryptedText)
menubar.add_cascade(label="Encrypter", menu=encryptermenu)
The checkbutton options, and now the functions:
def saveText():
sdtst = messagebox.askyesno(title="Save Text", message="A .txt file will be created at the same directory as this program to save the text you decided to encrypt. Is it ok?")
def saveEncryptedText():
sdtset = messagebox.askyesno(title="Save Encrypted Text", message="A .txt file will be created at the same directory as this program to save the encrypted text generated by this program. Is it ok?")
Should the checkbutton really run the function on-click or am I just making confusion?
Anyway, hope someone will help me.

Checkbutton Menu can definitely call a function. That is what the checkbutton option is there for :)
I generally use 2 functions to handle a call from the checkbutton menu.
first create that menu and assign it to a boolean var
yesno = BooleanVar(root)
mymenu.add_checkbutton(label="Do this ?", variable=yesno, command=mytoggle)
then you need 2 functions:
1) one callback to toggle
2) one to handle yes
def mytoggle(event=None):
val = yesno.get()
if val:
dosomething()
else:
somethingelse()

To answer your specific question, yes, the function will be called when you click on the checkbutton.
You should be calling add_command rather than add_checkbutton on the menu. Using a checkbutton to call a function from a menu is highly unusual and will likely confuse your users.

Related

Replacing input() portions of a Python program in a GUI

I am pretty new to python and have been working with a program that was originally meant for the command line. As such, it uses the input() function quite a bit, especially in the middle of loops. Mostly, the original code would do something like this:
for item in list:
# some logic here
user_input - input("prompt")
# uses user_input
I've decided I want to make a GUI for it, but I also want to make it optional. I have a class called Viewer that decorates the original program, and I am struggling to figure out how to best to handle the calls to input().
My first thought was just to inject a new input function altogether so that way it looks for input in my GUI text box instead the sys.stdout. I found many Stack Overflow answers on how to print sys.stdout to a GUI element (like this), but not how to take input from one. All of my attempts either ended in freezing the GUI by creating a loop, or the program not pausing for input and simply grabbing what was in the box when the prompt was put forward.
Secondly, I tried to break apart my functions to better match the code examples for buttons. So, button 1 would trigger a function_part_1, which would go until it required an input. If the GUI flag was turned off, it would automatically go to an input function, otherwise it would return and a button press would trigger function_part_2:
def function_part_1(list):
item = list[0]
# do some logic on item 1
if GUI:
print("prompt")
return
# After this, the GUI waits for a button press, and then calls function_part_2
else:
function_input(list)
def function_input(list):
user_input = input("prompt")
function_part_2(user_input, list)
def function_part_2(user_input, list):
# uses user_input on item
list.remove(list[0])
if list:
function_part_1(list)
else:
return
However, this turned ugly really quickly. First, it broke apart many loops which would require a lot of refactoring to keep the logic in tact (Especially for the nested loops). It also requires that I pass all my local variables from function-part to function-part, which exploded the function headers. In order to have only a single input box for the user, the GUI has to have logic to know which function is executing and what function-part comes next. It made my functions harder to follow and hurt readability.
Is there a nicer way to do this?
I am using appJar for the GUI, which is based around Tkinter. I am willing to switch to pure Tkinter if that would make things easier.
The easiest way is to overwrite the input function. Here's a working example using tkinter:
import tkinter as tk
def input(prompt=''):
win= tk.Tk()
label= tk.Label(win, text=prompt)
label.pack()
userinput= tk.StringVar(win)
entry= tk.Entry(win, textvariable=userinput)
entry.pack()
# pressing the button should stop the mainloop
button= tk.Button(win, text="ok", command=win.quit)
button.pack()
# block execution until the user presses the OK button
win.mainloop()
# mainloop has ended. Read the value of the Entry, then destroy the GUI.
userinput= userinput.get()
win.destroy()
return userinput
print(input('foobar'))

bind two commands for one button

Im a complete beginner for programming.I want to combine two processes with a single button 'ok' in tkinter.
I want the program to execute according to the user input. If someone inputs as Arrival(vehicle), I want the program to be executed in one way and if someone inputs as 'departure(vehicle)', I want the program to be executed in another way.
How can i do this with a single ok button?
This is my way and it won't work!When i enter the vehicle number, arrival method and departure method both will be executed!
def OkClicked1(event=None):
stepwindow.delete(0,END)
vehicle=str(txtEntrXpression.get())
Arrival(vehicle)
Departure(vehicle)
If you want to have different functionality for the same button in different cases you should have an Entry or text-box in your window. The user will specify some details that will help the program distinguish between an arrival and a departure.
(I am using Python 3.3)
You can use it in the following way:
textbox1 = tkinter.Entry(root)
textbox1.pack()
OkButton = tkinter.Button(root, text = "Ok", command = lambda: OkClicked(textbox1)
The last line will ensure that the textbox is passed to the OnClicked function.
Inside the OnClicked function you can have this:
def OnClicked(textbox1, event=None):
text = textbox1.get()
stepwindow.delete(0,END)
vehicle=str(txtEntrXpression.get())
if text == "Arrival":
Arrival(vehicle)
elif text == "Departure":
Departure(vehicle)
"Arrival" and "Departure" are just suggestions. Of course, you can have any string in place of them.
I hope this works for you.

Text box entry in a GUI

For a class I am coding a small Chess simulator with a GUI using python. To enter a move I would like to have a text box that you can type in, and once you press the btn move, it moves that piece.
Currently I have the GUI with radio buttons for each piece. Under the function for the btn to move them I have:
if btn == 4:
cmds.select('A4')
cmds.move()
I however do not know how to write a code for a simple text box in a gui or what the propper code to reference the text box is.
Q: How to code a simple working text box, and how to write the code so the functions can reference the gui.
We can create the text box using the textField command. To create a typical textField command:
my_textfield = cmds.textField()
In Maya, every UI element has a unique identifier name string. When we call the textField command in create mode, it will return the name of the textField it created. Here, my_textfield would be a Python variable that contains the name of the created textField that we can refer to later. To access the text value of this textField we would do something like:
text_entered = cmds.textField(my_textfield, query=True, text=True)
Here, to access the text entered in the textField, we are calling the textField command in query mode, by setting the query flag as True and setting the text flag as True. Setting text=True in query mode, i.e. query=True tells the command to return the current text value of the textField. Now text_entered would be the Python variable that will contain the text entered.
Text fields can take any sort of textual inputs that might need validation. To avoid validation troubles you could use the intField if you know that the inputs need to be only integers. (There is also a floatField.) Using an intField is also very similar to textField. To create one:
my_intfield = cmds.intField(minValue=1, maxValue=8)
minValue and maxValue are optional parameters that let you set the minimum and maximum values that this field can accept. To access the value entered:
val_entered = cmds.intField(my_intfield, query=True, value=True)
Have a look at the documentation for these two fields for more information on what else they offer:
textField
intField

Tkinter, Entry widget, is detecting input text possible?

I have an Entry widget on a simple calculator. The user can choose to enter an equation via the keypad. I was wondering if there was a way to detect a character(from the keypad in my case) being typed into the Entry widget. So, focus is on the widget, user presses '4', it comes up on the widget... can I detect this act, for basic purposes of logging the input?
Every time you press a key inside a Tkinter window, a Tkinter.Event instance is created. All you need to do is access that instance. Here is a simple script that demonstrates just how:
from Tkinter import Tk, Entry
root = Tk()
def click(key):
# print the key that was pressed
print key.char
entry = Entry()
entry.grid()
# Bind entry to any keypress
entry.bind("<Key>", click)
root.mainloop()
key (being a Tkinter.Event instance) contains many different attributes that can be used to get almost any type of data you want on the key that was pressed. I chose to use the .char attribute here, which will have the script print what each keypress is.
Yes. There are a few different ways to do this, in fact.
You can create a StringVar, attach it to the Entry, and trace it for changes; you can bind all of the relevant events; or you can add a validation command that fires at any of several different points in the sequence. They all do slightly different things.
When a user types 4, there's a key event with just the 4 in it (which doesn't let you distinguish whether the user was adding 4 to the end, or in the middle, or replacing a whole selected word, or…), and then a modification event is fired with the old text,* and then the "key" or "all" validation function is called with the (proposed) new text, and the variable is updated with the (accepted) new text (unless the validation function returned false, in which case the invalidcommand is called instead).
I don't know which one of those you want, so let's show all of them, and you can play around with them and pick the one you want.
import Tkinter as tk
root = tk.Tk()
def validate(newtext):
print('validate: {}'.format(newtext))
return True
vcmd = root.register(validate)
def key(event):
print('key: {}'.format(event.char))
def var(*args):
print('var: {} (args {})'.format(svar.get(), args))
svar = tk.StringVar()
svar.trace('w', var)
entry = tk.Entry(root,
textvariable=svar,
validate="key", validatecommand=(vcmd, '%P'))
entry.bind('<Key>', key)
entry.pack()
root.mainloop()
The syntax for variable trace callbacks is a bit complicated, and not that well documented in Tkinter; if you want to know what the first two arguments mean, you need to read the Tcl/Tk docs, and understand how Tkinter maps your particular StringVar to the Tcl name 'PY_VAR0'… Really, it's a lot easier to just build a separate function for each variable and mode you want to trace, and ignore the args.
The syntax for validation functions is even more complicated, and a lot more flexible than I've shown. For example, you can get the inserted text (which can be more than one character, in case of a paste operation), its position, and all kinds of other things… but none of this is described anywhere in the Tkinter docs, so you will need to go the Tcl/Tk docs. The most common thing you want is the proposed new text as the argument, and for that, use (vcmd, '%P').
Anyway, you should definitely play with doing a variety of different things and see what each mechanism gives you. Move the cursor around or select part of the string before typing, paste with the keyboard and with the mouse, drag and drop the selection, hit a variety of special keys, etc.
* I'm going to ignore this step, because it's different in different versions of Tk, and not very useful anyway. In cases where you really need a modified event, it's probably better to use a Text widget and bind <<Modified>>.
If you just need to do simple things without using trace module you can try
def objchangetext(self, textwidget):
print(textwidget.get()) #print text out to terminal
text1 = tk.Entry(tk.Tk())
text1.bind("<KeyRelease>", lambda event, arg=(0): objchangetext(text1))

Getting visible text from a QTextEdit in PyQt

This is related to another question I found here that seems to be inactive for a few months, so I think it's worth asking again.
I have created a simple QDialog that has a QTextEdit and a QPushButton. This pops up in my application when a user right-clicks and selects the option to "add comments". I want them to be able to write free-form text and I'll just save whatever they write as a long string with no concern for new lines, etc.
When the user clicks the button, it executes code like this:
self.connect(accept_button,QtCore.SIGNAL('clicked()'),lambda arg=str(view_textedit.toPlainText()): self.updateGroupComments(arg))
def updateGroupComments(self,new_comment_str):
print "Updating user comment to have new string: " + new_comment_str
self.group_entry.list_of_user_comments[self.currentFrameCounter] = new_comment_str
This is not detecting the TextEdit text that is visible (it only detects whatever the text edit text is set to when it is created). How do I make a simple command that returns the currently visible text from a QTextEdit. Again, the function
toPlainText()
is not working correctly... it doesn't find the currently visible text, only whatever text was on screen before changes or additions started being made by the user.
If this can't be done without subclassing and appealing to cursor positions, it makes the whole thing seem worthless... so please keep suggestions only to those implemented without subclassing or manipulating cursors. It should be really simple and straightforward to just return all currently visible text... what am I missing?
Objects that are being bound to default arguments are evaluated at the definition time. The function is working correctly, it returns whatever was in the text field when it was executed. Your code simply calls it at the wrong moment. If you want to use lambda, then do:
self.connect(
accept_button, QtCore.SIGNAL('clicked()'),
lambda: self.updateGroupComments(str(view_textedit.toPlainText()))
)
Or make view_textedit an instance attribute instead, and do simply
self.connect(
accept_button, QtCore.SIGNAL('clicked()'), self.updateGroupComments
)
And change updateGroupComments to call self.view_textedit.toPlainText instead of taking an argument.
BTW, this is not PyQt specific, this is how Python works in general.
To illustrate my last comment, that lambda can very well be replaced with:
def slot():
self.updateGroupComments(str(view_textedit.toPlainText()))
self.connect(accept_button, QtCore.SIGNAL('clicked()'), slot)

Categories