Hello everybody. I am trying to learn how to create GUI for my python programs using Glade. Now I'm facing the following problem : I have written something like the code below, and when I run it everything works fine except secondWindow doesn't show up. What am I doing wrong? Do I have to change any properties through Glade?

Here's my code :

class MyProg() :

	def on_button_clicked(self, widget, data=None) :

		def do_something(x, y) :
			
			# Code
	
			return True

		self.mainWindow.hide()
		self.secondWindow.show()
		isdone = do_something(x, y)
		if isdone :
			self.secondWindow.hide()
			self.thirdWindow.show()

	def __init__(self) :
		builder = gtk.Builder()
		builder.add_from_file("blahblah.glade")

		self.mainWindow = builder.get_object("mainwindow")
		self.secondWindow = builder.get_object("secondwindow")
		self.thirdWindow = builder.get_object("thirdwindow")
		builder.connect_signals(self)

if __name__ == '__main__' :
	myprog = MyProg()
	myprog.mainWindow.show()
	gtk.main()

Thank you in advance.

Recommended Answers

All 20 Replies

Your code looks ununderstandable, messed up indention?

Erm, no, indention is right.
Let me explain what it does.

When the program starts, mainWindow shows up. In mainWindow there is a button, and when it's clicked, on_button_clicked(...) executes. So, according to the code, mainWindow hides, secondWindow appears and do_something(...) executes. When do_something(...) ends, it returns a True value, which is checked in line 14. Then, if everything worked well, and do_something(...) has indeed returned True, secondWindow hides and thirdWindow appears.
Well, do_something(...) is defined inside on_button_clicked(...), I don't know if it's a right way to do it, but it works just fine.
Now, as I said in my first post, everything else works perfectly, but secondWindow never appears (thirdWindow does, though).

If you need any further explanations, please tell me.

What local variables of on_button_clicked is do_something accessing and why are the lines 11 and 12 inside it? where x and y are comming as I see them nowhere initialized. I would also write object as source of inheritance for the class, not nothing.

Also the convention is to put __init__ method as first method of class

I would at least remove isdone and put the function call in it's place. My pyglade experience is from arround 3 years ago, from my first experiments maybe I could install it, and check it out, but I have not the glade file.

Logically what you are trying to do, if I understand, is:

mine = MyProg()
mine.mainaction()
if mine.second():
   mine.third()

where x and y are comming as I see them nowhere initialized.

Ok, you're right, sorry. I forgot to mention that this is only a part of my code. I cut a few lines so that it'd only be the code needed for this post. So, variables x and y are actually defined inside on_button_clicked, before do_something is called. There is no problem there, do_something does its job perfectly.

What local variables of on_button_clicked is do_something accessing and why are the lines 11 and 12 inside it?

do_something has only access to x and y. Lines 11 and 12 are not inside do_something.

Also the convention is to put __init__ method as first method of class

I did that, but nothing changed.

I would also write object as source of inheritance for the class, not nothing.

What do you mean? Write class MyProg(object)? What does this do?

Finally, what I'm trying to do is :

- show mainWindow
 - when button is clicked : 
   1) hide mainWindow,
   2) show secondWindow,
   3) execute do_something,
   4) if do_something returns True :
	5) hide secondWindow,
	6) show thirdWindow

everything works fine except secondWindow doesn't show up

You hide it.

def do_something(x, y) :			
			# Code
			return True

		self.mainWindow.hide()
		self.secondWindow.show()
		isdone = do_something(x, y)

                ## isdone is always True so secondWindow is always hidden
		if isdone :
			self.secondWindow.hide()

And Tony is correct. Your indentation is terrible. If you want help, you should make your code as readable as possible. The first rule is to use spaces only instead of tabs. Most text processors/IDE's will convert tabs to spaces for you.

Can you reveal real purpose in words for what your program does, so we could suggest better way of coding it?

Ok, thank you for the replies.
Sorry about the indentation, I prefer using tabs, so I've got used to them, I hadn't realised they look so terrible... :-P

Anyway, the main purpose of my program is to copy the contents of file into another file (both files given by the user). That's what do_something does. So, while do_something is copying the files, I want a window to appear on the screen showing something like a "Please wait" message and a spinner. That's secondWindow.
thirdWindow actually displays a "Done!" message.

@ woooee : So, you mean that maybe I should initialize isdone with a False value? I did that, before self.secondWindow.show() but still nothing changed.

If there is any better way to do what I want, please let me know or give me some links. Thank you very much for your help!

So, while do_something is copying the files, I want a window to appear on the screen showing something like a "Please wait" message and a spinner

If you want to do two things at once, it requires threading or multiprocessing, I don't know if GTK/Glade has one built in or if you would use Python's. You could also update a progress bar after every file is copied, which would not require multiprocessing.

If you want to do two things at once, it requires threading or multiprocessing, I don't know if GTK/Glade has one built in or if you would use Python's. You could also update a progress bar after every file is copied, which would not require multiprocessing.

Oh, I don't have any clue about threading/multiprocessing, I'll check it out. So, that means that I can't run do_something while showing a window on screen in the meantime?
Now, about the progress bar, it was my first thought but I couldn't figure out how to make it work. I'd appreciate it if you could provide any links...
Anyway, thank you very much for taking time to help. :-)

What you would in other Gui, I would have two inputs window for specifying the user inputs and disable editing of the entries when processing starts. I would show message "Processsing, this can take several minutes..." in label of same window. Then I could start timer events to update the progress indicator until processing stops, except it would in practise probably need threading to update properly (actualy I would have need for such progress indicator with my code, using tkinter, now it is just blocked during processing like I told above)

You can just update a dialog box, or whatever, each time a file is copied. "Copied x of total files"

This is a __very__simple__ example of a progress bar, using multiprocessing to display the bar while another function counts down in a label. Perhaps you can adapt some of the code to pygtk.

from multiprocessing import Process
import time

try:
    import Tkinter as tk
except:
    import tkinter as tk

class ProgressBar():
    def __init__(self, root):
        self.root=root
        self.root.geometry("75x50+600+100")

    def mainloop(self):
        self.root.mainloop()

    def start_running(self):
        """ create the progress bar widget
        """
        self.top=tk.Toplevel(self.root)
        self.top.title("Progress Bar")
        self.top.geometry("+500+300")

        canvas = tk.Canvas(self.top, width=265, height=60)
        canvas.pack()

        rc2 = canvas.create_rectangle(15, 20, 245, 50, outline='blue', fill='gray')
        rc1 = canvas.create_rectangle(25, 20, 35, 50, outline='white', fill='blue')

        total=100
        x = 5
        while True:        ## move the small rectangle +5 or -5 units
            total += x
            if total > 310:
                x = -5
            elif total < 100:
                x = 5
            time.sleep(0.2)
            canvas.move(rc1, x, 0)
            canvas.update()

    def start_countdown(self):
        """ a separate process in a separate GUI
        """
        self.label_ctr = tk.IntVar()
        label = tk.Label(self.root, textvariable=self.label_ctr)
        label.pack()
        self.ctr=25
        self.root.after(750, self.update)

    def update(self):
        if self.ctr > 0:
            self.label_ctr.set(self.ctr)
            self.ctr -= 1

            # use recursion-generally a bad idea-but there are only 25 calls
            self.root.after(750, self.update)
        else:
            self.root.destroy()   ## destroy root when zero is reached

root = tk.Tk()

PB=ProgressBar(root)
pr2=Process(target=PB.start_countdown(), args=())
pr2.start()

pr1=Process(target=PB.start_running(), args=())
pr1.start()

## start mainloop in a separate process as a function of the class
## don't know if this is really necessary or not
## the theory is, it is detached from the other 2 processes and so
##    can react to both independently
## also the mainloop() process can be killed=shut down properly
pr3=Process(target=PB.mainloop(), args=())
pr3.start()

## safety clean up
pr1.terminate()
pr2.terminate()
pr3.terminate()

@woooee: Your program opened zillion copies of the progress bar and had to be closed forcefully, see screen capture.

Here looks to be one working, not multitasking progress bar for tkinter (sorry that we are little of the original topic domain), it does have interesting double call of update_idletasks.

Another interesting thing is that it sets the class name for Tkinter instead of window title. Task manager shows the program with that name.

I took out the unnecessary lambdas from after event and put normal parameters, and I prefer green over the orchid1 default color. Also I took out ugly line continuation characters, which are not needed inside parenthesis. While on it I also changed string production to use more sensible old style formatting instead of functions. Original version can be found at http://tkinter.unpythonic.net/wiki/ProgressMeter

'''Michael Lange <klappnase (at) freakmail (dot) de>
The Meter class provides a simple progress bar widget for Tkinter.

INITIALIZATION OPTIONS:
The widget accepts all options of a Tkinter.Frame plus the following:

    fillcolor -- the color that is used to indicate the progress of the
                 corresponding process; default is "green".
    value -- a float value between 0.0 and 1.0 (corresponding to 0% - 100%)
             that represents the current status of the process; values higher
             than 1.0 (lower than 0.0) are automagically set to 1.0 (0.0); default is 0.0 .
    text -- the text that is displayed inside the widget; if set to None the widget
            displays its value as percentage; if you don't want any text, use text="";
            default is None.
    font -- the font to use for the widget's text; the default is system specific.
    textcolor -- the color to use for the widget's text; default is "black".

WIDGET METHODS:
All methods of a Tkinter.Frame can be used; additionally there are two widget specific methods:

    get() -- returns a tuple of the form (value, text)
    set(value, text) -- updates the widget's value and the displayed text;
                        if value is omitted it defaults to 0.0 , text defaults to None .
'''

import Tkinter

class Meter(Tkinter.Frame):
    def __init__(self, master, width=300, height=20, bg='white', fillcolor='green',
                 value=0.0, text=None, font=None, textcolor='black', *args, **kw):
        Tkinter.Frame.__init__(self, master, bg=bg, width=width, height=height, *args, **kw)
        self._value = value

        self._canv = Tkinter.Canvas(self, bg=self['bg'], width=self['width'], height=self['height'],
                                    highlightthickness=0, relief='flat', bd=0)
        self._canv.pack(fill='both', expand=1)
        self._rect = self._canv.create_rectangle(0, 0, 0,
                                                 self._canv.winfo_reqheight(),
                                                 fill=fillcolor,
                                                 width=0)
        self._text = self._canv.create_text(self._canv.winfo_reqwidth()/2,
                                            self._canv.winfo_reqheight()/2,
                                            text='', fill=textcolor)
        if font:
            self._canv.itemconfigure(self._text, font=font)

        self.set(value, text)
        self.bind('<Configure>', self._update_coords)

    def _update_coords(self, event):
        '''Updates the position of the text and rectangle inside the canvas when the size of
        the widget gets changed.'''
        # looks like we have to call update_idletasks() twice to make sure
        # to get the results we expect
        self._canv.update_idletasks()
        self._canv.coords(self._text, self._canv.winfo_width()/2, self._canv.winfo_height()/2)
        self._canv.coords(self._rect, 0, 0, self._canv.winfo_width()*self._value, self._canv.winfo_height())
        self._canv.update_idletasks()

    def get(self):
        return self._value, self._canv.itemcget(self._text, 'text')

    def set(self, value=0.0, text=None):
        #make the value failsafe:
        if value < 0.0:
            value = 0.0
        elif value > 1.0:
            value = 1.0
        self._value = value
        if text is None:
            #if no text is specified use the default percentage string:
            text = '%2i %%' % round(value * 100)
        self._canv.coords(self._rect, 0, 0, self._canv.winfo_width()*value, self._canv.winfo_height())
        self._canv.itemconfigure(self._text, text=text)
        self._canv.update_idletasks()

##-------------demo code--------------------------------------------##

def _demo(meter, value):
    meter.set(value)
    meter._rect
    if value < 1.0:
        value = value + 0.005
        meter.after(50, _demo, meter, value)
    else:
        meter.set(value, 'Demo successfully finished')

if __name__ == '__main__':
    root = Tkinter.Tk(className='meter demo')
    m = Meter(root, relief='ridge', bd=3)
    m.pack(fill='x')
    m.set(0.0, 'Starting demo...')
    m.after(1000, _demo, m, 0.0)
    root.mainloop()

@woooee: Your program opened zillion copies of the progress bar and had to be closed forcefully, see screen capture.

It will open one GUI for each
pr2=Process(target=PB.start_running(), args=())
statement so either multiprocessing has been broken for years and you are the only one to catch it, or the code would have to be different than what was posted here.

Oh, lots of things to study now! Thanks!
Though, before getting deeper into progress bars, I want to figure out how to make the spinner work. I changed the code so that the spinner appears inside mainWindow now and when the process is done, a doneWindow appears with a "Done" message. I read a little about multiprocessing and I made two processes : one for starting the spinner, and one for copying the files. Which looks something like this :

class MyProg() :

    def on_button_clicked(self, widget, data=None) :
		
        def __init__(self) :
            builder = gtk.Builder()
            builder.add_from_file("blahblah.glade")

            self.mainWindow = builder.get_object("mainwindow")
            self.doneWindow = builder.get_object("donewindow")
            builder.connect_signals(self)

            def startSpinner() :
                # Code to start the spinner
                # and show a "Please wait" message.

            def do_something(x, y) :
                # Code (Copy files)
                return

            spin = Process(target=startSpinner(), args=())
            spin.start()

            cop = Process(target=do_something, args=(x, y))
            cop.start()

            # How can I know when the do_something function is finished?

                spin.terminate()
                cop.terminate()
                self.doneWindow.show()

if __name__ == '__main__' :
    myprog = MyProg()
    myprog.mainWindow.show()
    gtk.main()

Now, the spinner does show up and do_something does its job at the same time. What I have yet to do is to find out if do_something has finished, in order to hide the spinner and show the doneWindow. Is there a way to do that?? :confused:

@wooeee: I copy pasted again, running in Python 2.6.6 (main one because of psyco), Python 2.7.2.... No go (this is Windows Xp, just freshly installed new copy, as old HD got broken)

It looks strange for me that you invoke the start_down and start_running method instead of passing it as function to Process. start_running of course return None (does not return anything), then target is None.

The thing does not work at all from IDLE. If I pass the methods instead of invoking them, I get 'pickle.PicklingError: Can't pickle 'tkapp' object:'

The functions have to receive a reference to "self" to be part of the class. Also, the indentation is not correct, and the indentation starting with spin.terminate() is screwy as well. Something along these lines is closer but I can not test it right now.

class MyProg() :
 
    def __init__(self) :
            builder = gtk.Builder()
            builder.add_from_file("blahblah.glade")
 
            self.mainWindow = builder.get_object("mainwindow")
            self.doneWindow = builder.get_object("donewindow")
            builder.connect_signals(self)
 
            spin = Process(target=self.startSpinner(), args=())
            spin.start()
 
            cop = Process(target=self.do_something(), args=(x, y))
            cop.start()
 
    def on_button_clicked(self, widget, data=None) :
                spin.terminate()
                cop.terminate()
                self.doneWindow.show()

    def startSpinner(self) :
        # Code to start the spinner and show a "Please wait" message.
        while self.running:     ## conditioned by the variable set in do_something()
            #update spinner 

    def do_something(self, x, y) :
        # How can I know when the do_something function is finished?
        self.running=True

        # Code (Copy files)
         
        self.running=False 
        # shut down GUI = call self.on_button_clicked() ??

if __name__ == '__main__' :
    myprog = MyProg()
    myprog.mainWindow.show()
    gtk.main()

The thing does not work at all from IDLE

You can not run Tkinter apps in IDLE as IDLE is written in Tkinter and it falls and can't get up.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.