I am trying to code a program with a regular file menu. (e.g. File, Edit, View, etc).
I want the action they take in that menu to update my status bar (a label).
The problem is, the way I have it setup now, I believe it's executing the command and then trying to take the result as what it should do.
Currently a menu item is defined like so:
fileMenu.add_command(label="Insert", command=self.statusUpdater(statusLabel,"Insert Triggered")
And the function statusUpdater is defined as such:
def statusUpdater(self,status,commandName):
status.config(text=commandName)
status.update_idletasks()
So the problem is, right at the start of the program, the status changes to "Insert Triggered". What I want is for that to only happy once I have actually clicked "Insert"
From hints I've seen elsewhere it seems like I need some way to pass and handle the event of Insert being clicked.
Could someone supply a generic and basic function that does what I ask? I think the problem lies in the () attached to the command function, but I don't know any other way to pass arguments.
All i need is a function that is called on the click event, and knows which fileMenu command triggered it.
Thanks!
Commands take a reference to a function. You can se a lambda if you want to pass it arguments:
...command=lambda l=statusLabel, self.statusUpdater(l, "Insert Triggered"))
Related
I have an object of type QListWidget where the user can select 0 or more items.
If nothing is selected, how can i trigger an event that would call a function?
I know that you can detect clicking on a specific item by using:
QListWidget.itemClicked.connect(self.item_click)
Is there something similar for when nothing is selected at all? (or in other words, the QListWidget is clear)
Thanks!
Generally, you would connect to the itemSelectionChanged signal and then check whether anything is selected.
self.listwidget.itemSelectionChanged.connect(self.on_selection_changed)
def on_selection_changed(self):
if not self.listwidget.selectedItems():
# Do Stuff Here
self.nothing_selected_function()
But that will only catch events where something was selected and then the user deselected everything. If nothing was ever selected, it's not going to trigger this signal (like the first time you build the list, and nothing is selected). You'd have to call the slot manually in that case.
self.listwidget = ... # Code that builds and populates list widget
# Call this manually the first time.
self.on_selection_changed()
But part of your question is ambiguous. Why do you want to know when something is "not selected"? What about when a new item is added to the list? Should it trigger your "not selected" function since the list has changed, but there still isn't anything selected?
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! :)
So I have this very simple thing I wrote and it's killing me trying to figure out why it won't work. All it does it print a statement when you click.
So for the first example I had a button and assigned the function printName1 directly to it, which worked perfectly fine.
Then the next thing was to bind it using the .bind() function. So in this case we just have a frame that prints out certain things based on which button you press. But unfortunately whenever I use bind, it throws the error show above. References tkinter\__init__.py for the error, so it's not something directly in my code but maybe it needs to be done differently? Thanks guys.
from tkinter import *
root = Tk()
def printName1():
print('Jack')
def printName2():
print('John')
def printName3():
print('Jill')
frame = Frame(root, width=300, height=250)
frame.bind("<Button-1>", printName1)
frame.bind("<Button-2>", printName2)
frame.bind("<Button-3>", printName3)
frame.pack()
root.mainloop()
EDIT: The error is confusing because it made it seem like there was an extra argument when there should be 0. But actually I needed to add an argument to the functions and that was event. so it should be def printName1(event) and so on. Just figured I would let you guys know what worked for me in case anyone stumbles upon this.
If you refer to the documentation regarding tkinter events and bindings, you will see that when an event is triggered, the associated event object will be passed as the first (and only) argument to the bounded function (being printName1 and friends in your case).
So what you need to do is to modify those printName* functions to accept the event argument.
def printName1(event):
print('Jack')
Then what you desired to achieve should work.
Naturally, you could make the event argument optional as #TigerhawkT3 suggested.
Events, such as from the keyboard/mouse, are all sent to the application with information about the event: which key was it, where was the mouse when you clicked, that sort of thing. This means that any callback bound to such an event needs to take an argument. If you want to also bind it to a Tkinter Button, which doesn't take an event, you can handle that as well. Just define your functions with a default argument:
def printName1(event=None):
...
The issue
My issue is pretty simple, but I couldn't figure out how to cope with it easily even after some googling.
So I have a checkbutton:
self.but_val = IntVar()
self.but = Checkbutton(frame, text="text", variable=self.but_val)
This checkbutton triggers updates of some file path on the GUI:
self.but.bind('<ButtonRelease-1>',
lambda e: self.update_files_path(e), add='+')
In update_files_path(event), I need to get the value of the checkbutton to select the file paths to be displayed:
if self.but_val.get() == 0:
[...]
else:
[...]
The issue I have is that I get the value of the button before the clic.
And since the processing of file paths depends on different button values, I can't just use the opposite value.
My current work around
At the moment I have a function that is triggered before the clic and that save the state of the GUI:
self.but.bind('<ButtonPress-1>', lambda e,
self.save_design_opts_state(self.buttons_to_backup,
self.before_clic_vars_state), add='+')
Then in update_files_path(event) I call a function that infers the GUI state after the clic:
gui_state = self.get_gui_state(event)
This function is very annoying to implement, because I need to do a lot of things:
1- Check that the clic is really made on a button (to avoid a clic that starts on a button and end elsewhere!)
2- Get the value of the of all required buttons depending of their type
Is there an easier way to deal with this?
Thank you for your help!
Don't set your own bindings. Use the command option of the checkbutton. This option lets you specify a command to be run after the value has changed. There are other ways, but this is by far the simplest, most common way to solve your problem.
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)