I'm reading Programming Python by Mark Lutz, and I came across this bit of code that I don't get.
buttons = []
def onpress(i):
global state
state = i
for btn in buttons:
btn.deselect()
buttons[i].select()
I get what it's doing, but I don't get where these select and deselect methods are coming from. I've never seen these list methods before (I and the book are using Python 3). Are these builtin methods? And what do they do? I tried using my Google-fu to figure it out, but to no avail.
http://www.java2s.com/Tutorial/Python/0360__Tkinker/Deselectforradiobuttonssimplysetsthebuttonsassociatedvaluetoanullstring.htm
So a quick google, if you are doing this it means you are most likely using tkinter in which case you have missing relevant code somewhere.
Those methods .deselect() and .select() are actually tkinter Radiobuttons() methods. If you're tutorial reverences tkinter. "from tkinter import *" will import those class's. If not i have no idea why its being taken out of context.
But what this code does is, it takes 'i', which is a button you click, then it sets 'state' to 'i', making 'state' 'global' over writing the last 'state', then it deselects all buttons, and selects 'i' which is your new choice. In effect this means you'll only ever select 1 button
If you wonder why state is an empty string, its because an empty string is the first button in tkinter. When you pass a new string in, it gives the new item being something like "I001" or something.
Related
Is there any way to add options in an OptionMenu object without using the command flag?
I have seen many posts, such as this one, showing how the add_command will update/remove/add options to an already existing OptionMenu object. For example, this snippet of code removes all options in serialPortOptionMenu and repopulates the option menu with different options:
serialPortOptionMenu["menu"].delete(0, "end")
for serialPort in serialPortsArray:
serialPortOptionMenu["menu"].add_command(label=serialPort[1], command=lambda v=serialPort: serialPortFunc(v))
However, something like this seems to overwrite the original command flag I wrote when creating the OptionMenu object:
serialPortOptionMenuValue = Tkinter.StringVar(optionMenuFrame)
serialPortOptionMenuValue.set(serialPorts[0])
serialPortOptionMenu = Tkinter.OptionMenu(optionMenuFrame, serialPortOptionMenuValue, *serialPorts, command=lambda *args: callbackFuncWhenOptionMenuSelectsAnotherOption(*args))
serialPortOptionMenu.grid(row=3, column=0, columnspan=2, sticky="we")
As I'm sure many people are wondering why I am setting a command within an OptionMenu (weird I know), it is because I want a callback function to be called when the user picks a new option.
"What about the trace option"-Everyone...Yes, I am aware of this as well, but because I am reading/writing new values to the Tkinter variable in code without having the OptionMenu solely change it, using trace within Tkinter would not be an effective substitute for just tracking when the user selects a new option in the OptionMenu.
Why am I changing the Tkinter value in code and not having the OptionMenu do it? Because I thought it would be nice to modify the Tkinter value string with something like a ~ at the end of the string to signify something is happening behind the scenes that isnt completed yet, and only when it is completed will the ~ go away, hence the reading and writing to the value without having the OptionMenu solely change it.
What I am primarily interested in is if anyone knows of other ways to add options to an OptionMenu object without using
myMenu["menu"].add_command(..., command=...)
As it seems to be removing my original callback function.
Note: Having two OptionMenus and performing grid_remove/grid on them to hide/reveal them also crossed my mind, but just seems to messy.
There is no other way. An option menu is nothing more than a menubutton and a menu with a custom binding. You can create your own OptionMenu and add any methods you want.
I don't know if it will be useful to you anymore, but maybe somebody else will be looking for this as I was. You actually just need to put the command as a callback parameter in the command you use to add the item (I found tk._setit):
def refresh_dropdown():
# Reset var and delete all old options
var.set('')
dropdown['menu'].delete(0, 'end')
# Insert list of new options (tk._setit hooks them up to var)
new_choices = ['a', 'b', 'c', '...'] # you can make a dictionary[key_from_previous_dropdown.get()] as I did in my case
for choice in new_choices:
dropdown['menu'].add_command(label=choice, command=_setit(var, choice, new_command))
dropdown = OptionMenu(app, var, command=new_command)
I am new in Python and in tkinter so the question may seems naive: is it ok to create and place widgets at the same time if I don't need to change them?
It works but is it a good practice? And if not why?
An example of what I mean:
import tkinter as tk
window=tk.Tk()
tk.Label(window,text='Lost Label').pack()
window.mainloop()
To expand upon #Skynet's answer....
Whenever you do Widget(*args, **kwargs).pack() the pack() method returns None as would other geometry managers, so if you tried to assign this to a variable the variable would be None.
In this case then probably not, since you probably actually want to be storing the reference to the widget.
If you don't need a reference then there's not really a problem with it. As the other answer notes you don't need a definitve reference to every single widget in your GUI unless you plan to use this reference in some way. Unless I plan on changing the label text / modifying it in someway then I typically use your method to save some space. No need to write more code than you have to!
For example you're creating a Button widget.
btn = Button(blabla, text="Button1")
This returns a button object and if you need later to configure it or get information about it you can do it by through the btn variable.
But if you use something like btn = Button(blabla, text="Button1").pack() it returns None and not a button object so you won't be able to change anything about the button or get information about it later.
Another example is with the Entry widget
entry = Entry(blabla)
Using that later you can do entry.get() to get the text inside the entry
but you won't be able to do it if you use entry = Entry(blabla).pack() since it doesn't return an entry object, it just packs the widget and you won't be able to access it for later use.
There is nothing wrong with that approach and I have already seen it quite a few times. You don't have to keep a reference to every widget in your GUI.
The program I created allows for any letter on the keyboard the user types to be written on the turtle graphics canvas. In my program, I have also created a Python menu to which a Point object (for each letter function) is written to every time a user executes a function/draws a letter. However, because of the nature of my program, the user can also attach two of the same functions to the menu. If two of the same things get attached to the menu, for example two functions, I need a way to differentiate between them somehow. To do this, I have created a counter in another function and called that counter function in the function where the menu is written to, like so:
Counter function:
def increase():
if not hasattr(increase, "counter"):
increase.counter = 0
increase.counter += 1
Code block when menu is written to:
global loki
kli.config(state = NORMAL)
loki = ("{}".format(po.getfunction()))
increase() #<-- Counter function
undo1.add_command(label = str(increase.counter) + Point.__str__(po), command = lambda: selectundo(undo1.index(po)))
Point.__str__ is this method in the Point class:
def __str__(self):
return "({})".format(self.function)
However, I keep getting this error whenever I select something from the menu:
undo1.add_command(label = str(increase.counter) + Point.__str__(po), command = lambda: selectundo(undo1.index(po)))
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 2782, in index
i = self.tk.call(self._w, 'index', index)
tkinter.TclError: bad menu entry index "(<function draw_O at 0x105834d90>)"
I am thinking it has something to do with the following function, which undoes the function that is selected from the menu, but I am not sure:
def selectundo(x):
# This function undoes the function selected from the menu
for ty in range(x, len(function)):
undoHandler()
update()
listen()
Although, before I concatenated str(increase.counter) to Point.__str__(po), it worked perfectly.
So, what am I doing wrong here? Any help at all is much appreciated! :)
EDIT: Just to clear up what I am trying to do and why, I am trying to differentiate between two (or more) of the same functions if they are written to the menu and I am doing this because of the selectundo function (shown above) since, for example, if the user draws two (or more) of the same letter, I need to be able to differentiate between them, because right now, when I can't, the selectundo function undoes ALL instances of that letter, NOT just the first instance of what is pressed on the menu, which is what I actually want the program to do. If what I am trying to do to accomplish the task is not possible or if there is a better way to accomplish the task, please tell be about any/the other way that I can use to accomplish the task. I hope this helps alleviate any confusion! :)
My company is using Python 2.6 (yuck, I know, but it's my constraint). I need to make a little GUI that involves a ComboBox. I chose Tix, because that's what I have--not allowed to grab anything else.
Anyway, I'd like to set the label that the ComboBox has to the top side. According to the documentation at http://tix.sourceforge.net/dist/current/man/html/TixCmd/tixComboBox.htm, if I use "labelside" in the ComboBox constructor as a parameter, it should move the label to the top, like I want.
Unfortunately, when I do this, it gives me a strange error:
_tkinter.TclError: cannot assigned to static variable "-labelside"
CONTEXT: this is within a Python class that inherits from Tix.Frame. The first code example works perfectly, the other does not.
My constructor (not including 'labelside') looks like this:
combobox = Tix.ComboBox(self,
label="Available files: ",
selectmode='immediate',
dropdown=0,
editable=0,
variable=selectedfile,
options='listbox.height 5')
It works perfectly, as expected. I get a nice ComboBox in my window. The label is the left side, however--not what I want.
So, I try this:
combobox = Tix.ComboBox(self,
label="Available files: ",
labelside='top',
selectmode='immediate',
dropdown=0,
editable=0,
variable=selectedfile,
options='listbox.height 5')
That's when it gives me the error. I've scrounged the internet for answers, but have found only users who have the same unanswered question: why is it happening? It would appear that I'm following the documentation correctly.
I also tried substituting Tix.TOP for top, and it gave me the same error.
Any help or ideas would be greatly appreciated!
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)