QWidget not showing after calling show() - python

In My App, I have a QWidget which is not showing after I call show(), even though isVisible returns true.
This widget is created from an event of the main application window. But when its started on its own, i.e., as the only widget on an app, it shows up normally.
Anyone knows what may cause this behavior?
Other widgets in my app show up normally only this one is giving me troubles. It actually use to work just fine under a previous version of Qt4 (don't remember which).
the code for the widget is here
update: windows seems to appear and is immediately destroyed.

The relevant code is in hidx/GUI/main.py:
#pyqtSignature("")
def on_actionScatterplot_Matrix_activated(self):
...
spm = scatmat.ScatMat(pars, self.currentdbname)
print "==>", spm.pw.isVisible()
spm.pw.hide()
spm.pw.showMaximized()
print spm.pw.size()
print "==>", spm.pw.isVisible()
#pyqtSignature("int")
def on_rowStart_valueChanged(self, p0):
...
In on_actionScatterplot_Matrix_activated, you create an instance of ScatMat, but don't keep a reference to it. So the window will be briefly shown, and then immediately garbage-collected once the function completes.

Related

Python 2.7: Close a dialog without exiting the entire application

I'm running a script coded in python from a scripts menu in a desktop application. It's basically a giant macro that I wrote and added a GUI to. I'm pretty sure the GUI is a really old one that my desktop app uses called dialogKit from MIT.
GitHub still has it here.
The problem is the word "stop" at the very end of the dialog code.
I keep getting a "stop is undefined" message, which I understand, but I've tried everything to close the dialog and if I use exit(), sys.exit(), I don't get an error, but it also closes my entire desktop app.
I need to close the dialog and keep the software open.
The limited dialog documentation for what I'm using can be found here.
(you might have to click on the Dialog section. Their site uses frames.)
class MyDialog:
def __init__(self):
self.d = Dialog(self)
self.d.size = Point(300, 340)
self.d.Center()
self.d.title = "Halftone" #<----- Title of the dialogue
self.d.AddControl(STATICCONTROL, Rect(aIDENT, aIDENT, aIDENT, aIDENT), "frame", STYLE_FRAME)
# more controls and methods..
def on_ok(self, code):
return 1
def on_cancel(self, code):
print "blah"
def Run(self):
return self.d.Run()
d = MyDialog()
if d.Run()!= 1:
stop
I just need a way to change stop to something that 1) will prevent the script from running, and 2) close the dialog without quitting the entire application. This is the functionality of a typical "cancel" button, which is what I want.
Another option is the method called on_cancel(), which I also tried and could get the event itself to work, but still the entire application quits with any kind of exit().
The docs show a method called End(), which claims to terminate the dialog object, but I've tried and failed to get that to work either.
Okay, I'm posting an answer because I think I have a handle on your problem.
Try replacing stop with:
d.d.End()
If that works, you might want to try putting:
self.d.End()
inside of the on_cancel function in your class. That should close the dialogue without closing your program.

Some questions about switches in python

This is my first Question here at StackOverflow, so please be patient with me if some info isn't present or I missed something important, but anyways i'll do my best :)
Recently I started to code in Python2.7, so I'm not very good at it. While playing with PyGtk, PyGObject, Glade, etc I found something particular about switches (Haven't tried with any other widget, so I don't know if it happens somewhere else. Most likely it doesn't, I hope...)
I made a very basic GUI with a single "window" plus a "switch" using Glade
My objective was to deactivate switch after user tried to activate it if some exeption raised up before, something like:
Activate it --> * Found error --> * Deactivate it
I made some code, and after a while, I noted that THIS piece of code created a loop-like block, blocking GUI's window afterwards:
builder = Gtk.Builder()
window1 = builder.get_object('window')
switchie = builder.get_object('switchie')
switchie.set_active(False)
def Hi(switch, active):
print switchie.get_active()
switchie.set_active(not switchie.get_active())
switchie.connect("""notify::active""", Hi)
window1.set_position(Gtk.WindowPosition.CENTER)
window1.connect("delete-event", Gtk.main_quit)
window1.show_all()
If i'm right, "switchie.connect" links "switchie" object with "Hi" func whenever "switchie" gets clicked.
But if I execute this and try to turn switch on, GUI hangs up. I did try to execute this via script & command-line and adding the "print switch state", resulting in an endless loop (True & False)
I tried with many other funcs I made, but neither of them could solve this issue. In fact, this is the "essence" of all the other funcs I made.
Why does this happen?
Where's the loop?
Am I wrong in some line?
Help is appreciated!
(If you need to see the rest of my faulty funcs, just ask for 'em, but I don't think they'll help...)
You want to hook up the switch like this:
switchie.connect("""activate""", Hi)
This will only get called once for every time it is clicked. What you were doing is hooking up to the signal after it changed, so it was constantly changing, and never catching up. You will also want to change
def Hi(switch, active):
to
def Hi(switch, active = None):
for keyboard support.

PyQt5 QTableItem.setText() doenst do anything

I created my ui with Designer and converted it python code using pyuic5.
pyuic5 creates a QtableWidget instance which is called schedule, but doesnt init the cells so i inited them with:
for i in range(0,70):
item = QtWidgets.QTableWidgetItem()
self.schedule.setItem(i%14,math.floor(i/14),item)
And when i try to call setText() on them nothing happens. I check the internal value with text() right after I call setText() and it appears to be working fine but ui doesnt change.
for i in range(0,70):
self.ui.schedule.itemAt(i%14,math.floor(i/14)).setText(timeSlots[i])
print(self.ui.schedule.itemAt(i%14,math.floor(i/14)).text())
I thought about setting dimensions on QTableWidgetItem objects but i couldnt. It looks like they dont have a setGeometry() member.
I am running PyQt5 on Python2.7
Used .item() func instead of .itemAt() and it works.

Tkinter's event_generate command ignored

I am trying to figure out how to unittest a bind command in a dialog window. I'm attempting this with tkinter's event_generate. It is not working the way I expect. For this StackOverflow question I've set up some code with a single call to event_generate. Sometimes that line works and sometimes it is as if the line doesn't even exist.
The bind in the dialog's __init__ method looks like this:
self.bind('<BackSpace>', #Print "BackSpace event generated."
lambda event: print(event.keysym, 'event generated.'))
Any action in the dialog will call back to its terminate method (The dialog is based on Frederik Lundh's example Dialog in 'An Introduction to Tkinter'.)
def terminate(self, event=None):
print('terminate called') # Make sure we got here and the next line will be called
self.event_generate('<BackSpace>')
self.parent.focus_set()
self.destroy()
When the dialog is called using the code below any user action will end up calling terminate. In each case "terminate called" and "BackSpace event generated." are displayed. This proves that the call to event_generate is set up correctly.
parent = tk.Tk()
dialog = Dialog(parent)
dialog.wait_window()
In case it's relevant I ought to mention that I have moved Lundh's call to self.wait_window from his dialog's __init__ method to the caller. Whilst this breaks the neat encapsulation of his dialog it appears to be necessary for automated unittests. Otherwise the unittest will display the dialog and halt waiting for user input. I don't like this solution but I'm not aware of any alternative.
The problem I'm having is when wait_window is replaced with a direct call to the terminate method. This is the sort of thing that I'd expect to be able to do in unittesting which is to test my GUI code without running tkinter's mainloop or wait_window.
parent = tk.Tk()
dialog = Dialog(parent)
dialog.terminate()
This only prints "terminate called" and does not print "BackSpace event generated.". The call to event_generate appears to have no effect. If I follow the call in the debugger I can see that tkinter's event_generate() is being called with the correct arguments. self = {Dialog} .99999999, sequence = {str}'<BackSpace>', kw = {dict}{}
In view of the warning in the TkCmd man pages about window focus I have verified the dialog with the binding is given focus in its __init__ method.
Tkinter is not executing the callback. Why?
EDIT: This bare bones code shows update working. However, it only works if it is called in __init__ before event_generate is called by the main program. (This puzzle has been raised as a separate question)
class UpdWin(tk.Tk):
def __init__(self):
super().__init__()
self.bind('<BackSpace>',
lambda event: print(event.keysym, 'event generated.'))
self.update() # Update works if placed here
app = UpdWin()
app.event_generate('<BackSpace>')
# app.update() # Update doesn't work if placed here
Six Years On
4/12/2021. See Mark Roseman's excellent web site for a detailed explanation of why any use of update is a bad idea.
The problem posed by this six year old question is entirely avoided by better program design in which tkinter widget objects are never subclassed. Instead they should be created by composition where they can be easily monkey patched. (This advice is contrary to patterns shown in Frederik Lundh's example Dialog in 'An Introduction to Tkinter'.)
For unittest design, not only is there no need to start Tk/Tcl via tkinter but it is also unwise.
event_generate will by default process all event callbacks immediately. However, if you don't call update before calling event_generate, the window won't be visible and tkinter will likely ignore any events. You can control when the generated event is processed with the when attribute. By default the value is "now", but another choice is "tail" which means to append it to the event queue after any events (such as redraws) have been processed.
Full documentation on the when attribute is on the tcl/tk man page for event_generate: http://tcl.tk/man/tcl8.5/TkCmd/event.htm#M34
Don't know if this is relevant to your problem, but I got widget.event_generate() to work by calling widget.focus_set() first.
#lemi57ssss I know this is an old question, but I just want to highlight the point brought up by Bryan Oakley and to correct your last code to make it work. He said you have to update first before it can respond to the generated event. So if you switch the positions of update() and event_generate(), you will get the "BackSpace event generated." text printed out.
It worked when you put the update() in the __init__() was because of the same reason, i.e., it got called first before the event_generated().
See the amended code below:
class UpdWin(tk.Tk):
def __init__(self):
super().__init__()
self.bind('<BackSpace>',
lambda event: print(event.keysym, 'event generated.'))
#self.update() # Update works if placed here
app = UpdWin()
app.update() # Update also works if you placed it here
app.event_generate('<BackSpace>')

What is causing this cascade menu failure with tkinter in python?

The following code exhibits a problem I do not understand:
from Tkinter import *
root = Tk()
cheese_var = IntVar()
parrot_var = IntVar(value=1)
check_menu = Menu(tearoff=0)
check_menu.add_checkbutton(label="Cheese", variable=cheese_var)
check_menu.add_checkbutton(label="Parrot", variable=parrot_var)
count = 0
class Top():
def __init__(self):
global count
count += 1
self.tl = Toplevel(root)
Label(self.tl, text="Window " + str(count)).pack()
self.mb = Menubutton(self.tl, text="Push Me", bg='pink')
self.menu = Menu(self.mb, tearoff=0)
self.menu.add_cascade(label="Choices", menu=check_menu)
self.menu.add_command(label="New Window", command=new_top)
self.mb.config(menu=self.menu)
self.mb.pack()
def new_top():
Top()
Top()
root.mainloop()
The menu brought up by the menu button in the created top level window initially behaves as expected. Clicking on the New Window command there creates a new such window, which also behaves as expected. Indeed, as long as you keep creating new top level windows, everything continues to work as expected. However, once you delete (close) any one of those windows, then, in a subsequently created new window, the Choices cascade on the new menu is not functional. (It is still OK in the windows created before the closing of one.)
The situation in which I initially encountered this symptom was much more complex, but I was able to simplify it down to the above example which exhibits the issue. I have discovered that I can avoid the problem by having each instance of Top create its own check_menu as an attribute; but I do not understand why this should be necessary. Please point me the way if there is one to avoid the problem without such replication of a cascade menu used in multiple windows.
Unfortunately, I don't think it is possible to do what you want. I'll try to explain as best as I can.
When you first run the script, check_menu is created and works fine for the first window. As you create more windows, check_menu is simply shared between them. However, when you close one of them, check_menu (and everything under it) is destroyed. So, when you create a new window after that, check_menu no longer exists and it doesn't show.
However, the script doesn't throw an error because, for some reason, Tkinter allows you to assign menus to things that aren't menus. Believe it or not, none of the following code:
self.menu.add_cascade(label="Choices", menu=None)
self.menu.add_cascade(label="Choices", menu=1)
self.menu.add_cascade(label="Choices", menu="")
will break the script. Each line simply does nothing but create an empty cascade "Choices".
That is basically what is happening. After closing one window, check_menu and everything under it is destroyed. Yet, Tkinter doesn't throw an error but instead assigns a menu to something that is no longer a menu (as far as what it is assigning the menu to, I believe it is using the old instance of check_menu, which was destroyed).
To solve this problem, recreate check_menu and everything under it each time you call Top. In other words, put the code for check_menu (and its options) in the __init__ method of Top. That way, each time Top is called, check_menu will exist.
Hope this helps (and that I explained it sufficiently :)

Categories

Resources