Unable to change widget inside StackedWidget - python

I am trying to replace a widget for another. I am using a StackedWidget. I have the following.
First, I register add some widgets to the StackedWidget:
self.stackedWidget.addWidget(w1)
self.stackedWidget.addWidget(w2)
self.stackedWidget.addWidget(w3)
The I bind the click of a button of w1:
QObject.connect(w1.pushButton,SIGNAL("clicked()"),self.stackedWidget,SLOT(self.stackedWidget.setCurrentWidget(w2)))
For Slot I have also tried "setCurrentIndex". I checked if the signal is being received, and it is ok.
Finally, I show w1.
self.stackedWidget.setCurrentWidget(w1)
Although the "clicked()" signal is received when the button of w1 is pressed, the widget w2 never appears in the StackedWidget.
UPDATE:
I am doing
QObject.connect(w1.pushButton,SIGNAL("clicked()"),self.stackedWidget,SLOT('w1Clicked()'))
as suggested; however, I get
Object::connect: No such slot QStackedWidget::w1Clicked()
Object::connect: (sender name: 'pushButton')
Object::connect: (receiver name: 'stackedWidget')
I guess I have to create the slot "w1Clicked" somehow, but I am using the designer I can't figure it out.
I have one Main Window with the StackedWidget and separate forms with the buttons, so I don't see how to make the connection or create slots.
Also, I discovered that the problem with the way I was doing:
QObject.connect(w1.pushButton,SIGNAL("clicked()"),self.stackedWidget,SLOT(self.stackedWidget.setCurrentWidget(w2)))
is that "self.stackedWidget.setCurrentWidget(w2)" gets executed immediately, it does not wait for the signal! That's why w2 was never shown.
Still I have no idea.

Your connect call is wrong. You can't define what values are going to be passed to your slot when you make the connection. Instead you need to create your own slot and handle the signal as you desire (apologies if my python syntax is off):
QObject.connect(w1.pushButton,SIGNAL("clicked()"),self,SLOT("w1Clicked()"))
def w1Clicked(self):
self.stackedWidget.setCurrentWidget(w2)

Finally, it worked this way:
w1.pushButton.clicked.connect(self.w1Clicked)
At least the signal is received and the method is called correctly.

Related

In pyQt4 what is the difference between clicked() and stateChanged() signals for QCheckBox?

In my experimenting I have found that in some cases the graphical check display can be changed without stateChanged being sent, but clicked appears to always go. What is the difference?
In particular, something like this:
checkBox.stateChanged.connect(func)
def func(state):
print "Not allowed!"
checkBox.setChecked(not bool(state))
Does not work with stateChanged. It appears to get into func every other time only. What is going on? If I replace stateChanged with clicked, it works as expected.
Don't forget that you can change checkbox state programmatically. checkbox.setChecked(True) will emit stateChanged but not clicked

How to bind all frame widgets to <'Enter'> event

I the following code I want to bind all frame1 items to <'Enter'> Event, but it does not work. I mean canvas.focus_set() does not take effect. How can I solve my problem?
for w in frame1.winfo_children():
w.bind('<Enter>',canvas1.focus_set())
The comment made by Lafexlos actually sends you in the right direction. When you do
w.bind('<Enter>', canvas1.focus_set())
you call canvas1.focus_set() and use the return value of this function call (which is None) to bind to the event. This isn't what you want, because now every time the event is triggered, None is executed instead of canvas1.focus_set().
What you should do is pass a function reference to the bind function. The reference for calling canvas1.focus_set() is canvas1.focus_set. However, using
w.bind('<Enter>', canvas1.focus_set)
still doesn't work.
This is because the bind function passes an event object to the function it has been given, so it will call canvas1.focus_set(event) instead of canvas1.focus_set(). Because focus_set does not accept any arguments, this fails.
You can fix this in two ways. You could make an extra function, which does accept an event object and then calls canvas1.focus_set() without arguments, and then bind the event to this new function. The other option is to use an anonymous "lambda" function to basically do the same like
w.bind('<Enter>', lambda e: canvas1.focus_set())
This way the lambda function accepts the event object as e, but doesn't pass it to focus_set.
P.S. The <Enter> event is not the event that is triggered when you press the Enter button on your keyboard (that is <Return>). The <Enter> event is triggered whenever you move the mouse onto a widget and is accompanied by the <Leave> event for when you leave the widget with your mouse. This might be what you want, but it often leads to confusion.
by using canvas1.bind_all which is the parent of frame1 I solved my problem. Thanks for all solutions.
If there is any mistake I see you making it is likely you are not calling the write command for the Enter key. Hopefully, if you are attempting to do this on windows, you should rather use Return.
More like:
for w in frame1.winfo_children():
w.bind('<Return>',canvas1.focus_set())

Python Tkinter - How do I change variables from outside the main window class?

Now i understand the concept of instance variables and classes, I've never had a problem with them before and I use them frequently. However when I make my MainWindow class, everything is peachy until i try accessing instance variables.
http://pastebin.com/tDs5EJhi is the full code, but at this point it's just placing labels and frames and whatnot, no actual logic is going on. The window looks fine and nothing bad happens.
My question comes to be when I try changing things inside of the window externally. I figured I could just make an instance of the class and change variables from there (namely instancevariable.ImageCanvas.itemconfig()) like i can normally, but Tkinter isn't being nice about it and I think it's a result of Tkinter's mainloop().
Here's the tidbit of my class MainWindow() that i'm having trouble with (ln 207)
...
self.C4 = Tk.PhotoImage(file="temp.png")
self.card4 = self.CardCanvas.create_image(120,46,image=self.C4, state=Tk.NORMAL)
#self.CardCanvas.itemconfig(4, state=Tk.HIDDEN) # < It works here
...
self.root.mainloop()
window = MainWindow()
window.CardCanvas.itemconfig(4, state=Tk.HIDDEN) # < It doesn't work here
That's how i learned how to edit instance variables. When the window pops up, the itemconfig command doesn't actually apply like it would were it inside the class (or maybe it did and the window just didn't update?) and after closing the window I get this error:
_tkinter.TclError: invalid command name
which I assume is just because it's trying to apply a method to variables that don't exist anymore, now that the window has closed.
So I guess here's my big question - I have a MainWindow class, and from what I can tell, nothing can be changed from outside of the class because the Tk.mainloop() is running and won't stop to let other code after it run, like the itemconfig. How do I go about changing those variables? Code after the instance variable declaration doesn't seem to run until the MainWindow() is closed.
You are correct that code after mainloop doesn't run. It does, but only after the GUI has been destroyed. Tkinter is designed for the call to mainloop be the last (or very nearly last) line of executable code. Once it is called, all other work must be done as reaction to events. That is the essence of GUI programming.
The answer to "how do I go about changing the variables" is simple: do it before you call mainloop, or do it in reaction to an event. For example, do it in a callback to a button, do it in a function bound to an event, or to a time-based event via after, and so on.

Why do I get a runtime error with two linked gtk toggle buttons?

I have two toggle buttons that I want to link together so that pressing one releases the other one. So my signals look like this
def on_btn1_tgl_clicked(self, widget, data=None):
toggled = not self.btn2_toggle.get_active()
self.btn2_toggle.set_active(toggled)
def on_btn2_tgl_clicked(self, widget, data=None):
toggled = not self.btn1_toggle.get_active()
self.btn1_toggle.set_active(toggled)
This seems to work just as I expect, except that when I run the code I get this error
Error in sys.excepthook:
RuntimeError
Original exception was:
RuntimeError
The buttons work fine and nothing crashes, but I don't know why I'm getting the error, or what it means. If I don't link the buttons I don't get any errors.
Does anyone know how to fix this?
When I run your code, I get this exception :
RuntimeError: maximum recursion depth exceeded and the buttons doesn't work.
My explication is :
When you click on button 1, it emit the toggled signal and execute your on_btn1_tgl_clicked() method.
In this method, you use gtk.ToggleButton.set_active() which emit a toggled signal too that call your on_btn2_tg1_clicked() method which emit also a toggled signal.
So, we enter in infinite loop. That's why the exception is raised.
Just check before you set the button's state, whether it already has the state you are setting it to.
Why do you want to change the state of button every time it is changed by user? If you want to prevent user from changing, use widget.set_sensitive(False). Or if you want to prevent changing state based on some conditions, I think you can return True in on_btn1_tgl_clicked to say GTK "I already handled this event, so you dont handle it yourself", and GTK will not change its state. If you return False or nothing, GTK will handle it and changes its state.
I ended up solving this by setting the second button to the opposite of the first one, instead of the opposite of its current state. So something like
def on_btn1_tgl_clicked(self, widget, data=None):
toggle_val = self.btn1_toggle.get_active()
self.btn2_toggle.set_active(not toggle_val)
def on_btn2_tgl_clicked(self, widget, data=None):
toggle_val = self.btn2_toggle.get_active()
self.btn1_toggle.set_active(not toggle_val)
This doesn't seem to suffer from the recursion problems of the other solution.

PyQt sending parameter to slot when connecting to a signal

I have a taskbar menu that when clicked is connected to a slot that gets the trigger event. Now the problem is that I want to know which menu item was clicked, but I don't know how to send that information to the function connected to. Here is the used to connect the action to the function:
QtCore.QObject.connect(menuAction, 'triggered()', menuClickedFunc)
I know that some events return a value, but triggered() doesn't. So how do I make this happen? Do I have to make my own signal?
Use a lambda
Here's an example from the PyQt book:
self.connect(button3, SIGNAL("clicked()"),
lambda who="Three": self.anyButton(who))
By the way, you can also use functools.partial, but I find the lambda method simpler and clearer.
As already mentioned here you can use the lambda function to pass extra arguments to the method you want to execute.
In this example you can pass a string obj to the function AddControl() invoked when the button is pressed.
# Create the build button with its caption
self.build_button = QPushButton('&Build Greeting', self)
# Connect the button's clicked signal to AddControl
self.build_button.clicked.connect(lambda: self.AddControl('fooData'))
def AddControl(self, name):
print name
Source: snip2code - Using Lambda Function To Pass Extra Argument in PyQt4
use functools.partial
otherwise you will find you cannot pass arguments dynamically when script is running, if you use lambda.
I'd also like to add that you can use the sender method if you just need to find out what widget sent the signal. For example:
def menuClickedFunc(self):
# The sender object:
sender = self.sender()
# The sender object's name:
senderName = sender.objectName()
print senderName
In general, you should have each menu item connected to a different slot, and have each slot handle the functionality only for it's own menu item. For example, if you have menu items like "save", "close", "open", you ought to make a separate slot for each, not try to have a single slot with a case statement in it.
If you don't want to do it that way, you could use the QObject::sender() function to get a pointer to the sender (ie: the object that emitted the signal). I'd like to hear a bit more about what you're trying to accomplish, though.

Categories