Hi
I have been doing text programming for a while now and i was going to start doing some graphical interfaces for my programs but i was wondering which one is the best for beginners and also what and ther advantages/disadvantages of each of them.
Thanks

Recommended Answers

All 18 Replies

Tkinter is the easier, and has one big advantage; its standard.

However i personally find hat wxPython is better although slightly harder to learn.

Chris

The wxPyton GUI toolkit has the fancier widgets for animation, graphics, plotting, sound and so on. If you are not at all familiar with GUI programming, Tkinter is a good way to start with and get the hang of it. Tkinter has a lot of sample code.

Depends a little on what applications you want to write. Tkinter may be all you ever need. Once things get more demanding you then can switch to wxPython, PyGTK or PyQT.

Knowing Tkinter will make the learning curve of these other toolkits a little easier.

I learnt wxPython yesterday. I tried Tkinter at first, but disliked it. Here's a summary of why: http://www.wxpython.org/quotes.php

It's really easy to learn and understand. The trouble is there's little sample code and many tutorials on it are out of date because it seems to change a fair bit each revision.

I recommend this post here as a good reference: http://www.daniweb.com/forums/post335474-3.html

You might also be interested in my application which is fairly simple but a good example of a working wxPython app: http://www.daniweb.com/forums/thread127935.html

yeah. Wx certainly looks good. I take it it is a lot more powerful than Tkinter in the end too. I was looking for some tutorials on it but i cannot find any that go slowly from the start. I just want a tutorial that doesn't rush through the start.

Then let me write one for you:

import wx

app = wx.App() # create a wx application

window = wx.Frame(None, title = 'Sample GUI App') # create a window
btn = wx.Button(window) 
# create a button 'widget' on the window - note how the button receives the window. This creates a tree-like structure where the window is the parent node and anything else like buttons are child nodes.

window.Show() # make the frame and button visible
app.MainLoop() # keep things going

This makes a window. It then puts a button on the window. Yes, the button takes up the whole window. Have a read, understand it, fiddle if you can, then go on. Please compile this code now to confirm it runs. Onwards:

import wx

app = wx.App()

window = wx.Frame(None, title = 'Sample GUI App',
                  pos = (100,100), size = (400,500))
helloBtn = wx.Button(window, label = 'Hello',
                pos = (400 - 60 - 15, 10), size = (60,25))
byeBtn = wx.Button(window, label = 'Bye',
                pos = (400 - 120 - 15, 10), size = (60,25))
printArea = wx.TextCtrl(window,
                        pos = (10, 10), size = (400 - 120 - 15 - 10, 25),
                        style = wx.TE_READONLY)
window.Show()
app.MainLoop()

Compile and run this code first. Now look at the placement of widgets and what they are. Notice wx.TextCtrl is a text box. It's quite flexible - allowing multiline text with scroll bars, read only mode, write mode, etc. I've set it to read only for what I am about to do with it.

Also look at the position and size parameters. Fiddle with them a bit. Compile and run, etc. Press the buttons. Note how they do nothing? That's because we need 'event handlers'.

Before I go on to event handlers, I'd like to make this GUI more flexible. For example, press the maximise button. Notice how the progamme doesn't expand to fit the screen? Let's fix that. It's fairly simple. Just add an extra node in the tree; put a layer between the window frame and the widgets. It's called a 'sizer':

import wx

app = wx.App()

window = wx.Frame(None, title = 'Sample GUI App',
                  pos = (100,100), size = (400,500))
background = wx.Panel(window)


loadBtn = wx.Button(background, label = 'Load')
transferBtn = wx.Button(background, label = 'Transfer')
inputArea = wx.TextCtrl(background)
transferArea = wx.TextCtrl(background, style = wx.TE_READONLY | wx.TE_MULTILINE)


horizontalBox = wx.BoxSizer()
horizontalBox.Add(inputArea, proportion = 1, border = 0)
horizontalBox.Add(transferBtn, proportion = 0, border = 0)
horizontalBox.Add(loadBtn, proportion = 0, border = 0)

verticalBox = wx.BoxSizer(wx.VERTICAL)
verticalBox.Add(horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0)
verticalBox.Add(transferArea, proportion = 1, flag = wx.EXPAND, border = 0)

background.SetSizer(verticalBox)
window.Show()
app.MainLoop()

Notice how the 'tree' is preserved? You can eventually trace the buttons and text box back to the parent window through the background widget.

Remember, anything starting with a lower case letter tends to be an arbitrary variable name which you can rename to whatever you want. It is the things with capitals at the start that are important. Conventionally (at least in C++, Java and Haskell), you name variables and functions: thisIsAVariable, and classes: ThisIsAClass. So you could rename 'window' and 'background' to anything you wanted. Same applies to the widget names.

Have a fiddle with that. Sizers are a bit complex at first but you should use them instead of statically defining the size and start co-ords. Note the hierarchy: horizontalBox is a child of verticalBox. Position in the programme depends on location in the code. The first

Note I changed the button and textbox names and added a new textbox. Examine the 'style' parameters. One is input one is for output. The output box is also multiline - like word wrap.

Resize the window and confirm it works.

The wx.VERTICAL value indicates the sizer is vertical. Otherwise the default is horizontal. Proportion=0 means go small as possible. If a widget had proportion = 1 and another had proportion = 2, then the first widget would take up 1/3 of the space (1+2 = 3) whilst the second would take up 2/3, after the proportion = 0 widgets had been considered.

One more thing before event handling... let's make our code more shall we say 'standard':

import wx

# always declare constants in capitals and as global variables at the start of your code

WINDOW_WIDTH = 400
WINDOW_HEIGHT = 500

class MainFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title = 'Sample GUI App',
                          pos = (200,75), size = (WINDOW_WIDTH, WINDOW_HEIGHT))
        
        self.background = wx.Panel(self)

        self.loadBtn = wx.Button(self.background, label = 'Load')
        self.transferBtn = wx.Button(self.background, label = 'Transfer')
        self.inputArea = wx.TextCtrl(self.background)
        self.transferArea = wx.TextCtrl(self.background, style = wx.TE_READONLY | wx.TE_MULTILINE)

        self.horizontalBox = wx.BoxSizer()
        self.horizontalBox.Add(self.inputArea, proportion = 1, border = 0)
        self.horizontalBox.Add(self.transferBtn, proportion = 0, border = 0)
        self.horizontalBox.Add(self.loadBtn, proportion = 0, border = 0)

        self.verticalBox = wx.BoxSizer(wx.VERTICAL)
        self.verticalBox.Add(self.horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0)
        self.verticalBox.Add(self.transferArea, proportion = 1, flag = wx.EXPAND, border = 0)

        self.background.SetSizer(self.verticalBox)
        self.Show()
    
app = wx.App()
window = MainFrame()
app.MainLoop()

Thrown in a class. That's about it. Look over it, compile it, run it, understand it, etc. Now on to the good stuff.

Event Handling

import wx

# always declare constants in capitals and as global variables at the start of your code

WINDOW_WIDTH = 400
WINDOW_HEIGHT = 500

class MainFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title = 'Sample GUI App',
                          pos = (200,75), size = (WINDOW_WIDTH, WINDOW_HEIGHT))
        
        self.background = wx.Panel(self)

        self.loadBtn = wx.Button(self.background, label = 'Load')
        self.loadBtn.Bind(wx.EVT_BUTTON, self.loadEvent)
        
        self.transferBtn = wx.Button(self.background, label = 'Transfer')
        self.transferBtn.Bind(wx.EVT_BUTTON, self.transferEvent)
        
        self.inputArea = wx.TextCtrl(self.background)
        self.transferArea = wx.TextCtrl(self.background, style = wx.TE_READONLY | wx.TE_MULTILINE)

        self.horizontalBox = wx.BoxSizer()
        self.horizontalBox.Add(self.inputArea, proportion = 1, border = 0)
        self.horizontalBox.Add(self.transferBtn, proportion = 0, border = 0)
        self.horizontalBox.Add(self.loadBtn, proportion = 0, border = 0)

        self.verticalBox = wx.BoxSizer(wx.VERTICAL)
        self.verticalBox.Add(self.horizontalBox, proportion = 0, flag = wx.EXPAND, border = 0)
        self.verticalBox.Add(self.transferArea, proportion = 1, flag = wx.EXPAND, border = 0)

        self.background.SetSizer(self.verticalBox)
        self.Show()

    def loadEvent(self, event):
        fileContents = open(self.inputArea.GetValue(), 'rb')
        self.transferArea.SetValue(fileContents.read())
        fileContents.close()

    def transferEvent(self, event):
        self.transferArea.SetValue(self.inputArea.GetValue())
    
app = wx.App()
window = MainFrame()
app.MainLoop()

I added two Bind commands and two methods/functions to the class. That's it. Well, have fun!

(The programme transfer whatever you type into the small box, inputArea, into the larger box, transferArea once you press 'Transfer'. Alternatively, you can type a file location into the inputArea and press load and it will load that file into the transferArea.)

If you have any further inquiries, do a google search, read a few forum threads, and most importantly read the documentation for the method or class in question. Feel free to ask me (and others) here, too, but try and make it a last resort - you learn more through independence, generally.

commented: great intro to wxPython +8

Fuse, could you do us all a favor and also post your nicely written introduction to wxPython in the "Starting Python" sticky. I enjoyed your style and clean code. You can do it all in one post or several posts.

Thank you!

Sure thing! Glad you liked it.

I'll clean it up tomorrow (or soonish) as there are a few errors and typos, as well as elaborate on some points (such as using multiple horizontal box sizers for more complex widget positioning). I'm also teaching myself about some other widgets, so I'll add them if I feel they could do with explaining.

Sure thing! Glad you liked it.

I'll clean it up tomorrow (or soonish) as there are a few errors and typos, as well as elaborate on some points (such as using multiple horizontal box sizers for more complex widget positioning). I'm also teaching myself about some other widgets, so I'll add them if I feel they could do with explaining.

The best way to learn is to teach a subject. I have enjoyed your presentation of wxPython too.

Compared to Tkinter, wxPython's power is in its clever widgets, a more pythonic approach.

The best way to learn is to teach a subject. I have enjoyed your presentation of wxPython too.

Compared to Tkinter, wxPython's power is in its clever widgets, a more pythonic approach.

I hear you! And I'm not sure what this mysterious 'Pythonic' coding style is, but I suspect it is generally how I intuitively code, due to my first programming experiences being Java's pure OO paradigm and Haskell's pure functional paradigm.

As a side note, I highly recommend Haskell to everybody (especially anybody who enjoys Python). There's even a wxHaskell implementation, which mainly uses monads to emulate imperative programming. Not the best language to code in, but an invaluable language to learn to improve your programming ability and style. As a bonus, Haskell is white-space sensitive like Python, and has a beautiful standard library (better than or equal to Python's in my opinion), so it is understandably easy to learn once you adjust to the functional paradigm.

In fact I have a feeling Python was influenced by Haskell or something similar, because it has white-space sensitivity, list comprehensions, lambda functions, lists, tuples, strict typing, an interpreter, etc. I learnt from an excellent book: 'Haskell: The Craft of Functional Programming', if anyone's interested. In fact, I'd probably describe Python as Haskell + Java.

I think the Pythonic approach is the use of powerful, optimized modules. So yeah, wxPython uses powerful, optimized widgets.

Python has clearly borrowed the best of several earlier languages.

Maybe we need a new sticky called "Starting wxPython" for the GUI programmers.

I think so, too. Up to vegasat.

Maybe we need a new sticky called "Starting wxPython" for the GUI programmers.

Okay the sticky has been created. Let's hope it will be a success.

Fuse,
I tested all the code in your tutorial for wxPython that you so kindly contributed to the new "Starting wxPython" sticky. The code examples work like a charm on my Windows Vista machine.

Phew! I thought you were going to say they don't compile. Cheers. :D

yeahs its all really good and great to learn with. Well done and thanks. :)

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.