wxPython - Creating a Simple Image Viewer
The first step in creating an application is to come up with an idea. You could try to copy something simple like Microsoft Paint or Notepad. You will quickly find that they aren’t so easy to emulate as you would think, though! So instead, you will create a simple application that can load and display a photo.
When it comes to creating a GUI application, it is a good idea to think about what it will look like. If you enjoy working with pencil and paper, you could draw a sketch of what your application will look like. There are many software applications you can use to draw with or create simple mock-ups. To simulate a Sizer, you can draw a box.
Here is a mockup of what the finished application should look like:
Now you have a goal in mind. This allows you to think about how you might lay out the widgets. Go ahead and create a new file named image_viewer.py and add the following code to it:
Here you create a new class named ImagePanel() that will hold all your widgets. Inside it, you have a wx.Image, which you will use to hold the photo in memory in an object that wxPython can work with. To display that photo to the user, you use wx.StaticBitmap. The other widget you need is the familiar wx.Button, which you will use to browse to the photo to load.
The rest of the code lays out the widgets using a vertically oriented wx.BoxSizer. You use the sizer’s Fit() method to try to make the frame “fit” the widgets. What that means is that you want the application to not have a lot of white space around the widgets.
When you run this code, you will end up with the following user interface:
That looks almost right. It looks like you forgot to add the text entry widget to the right of the browse button, but that’s okay. The objective was to try and get a close approximation to what the application would look like in the end, and this looks pretty good. Of course, none of the widgets actually do anything yet.
Your next step is to update the code so it works. Copy the code from the previous example and make a new file named image_viewer_working.py. There will be significant updates to the code, which you will learn about soon. But first, here is the full change in its entirety:
This change is pretty long. To make things easier, you will go over each change in its own little chunk. The changes all occurred in the ImagePanel class, so you will go over the changes in each of the methods in turn, starting with the constructor below:
There are a few minor changes here. The first one is that you added a max_size for the image. Then you hooked up an event to the the browse button. This button will now call on_browse() when it is clicked.
The next change is that you added a new widget, a wx.TextCtrl to be precise. You stored a reference to that widget in self.photo_txt, which will allow you to extract the path to the photo later.
The final change is that you now have two sizers. One is horizontal and the other remains vertical. The horizontal sizer is for holding the browse button and your new text control widgets. This allows your to place them next to each other, left-to-right. Then you add the horizontal sizer itself to the vertical main_sizer.
Now let’s see how on_browse() works:
Here you create a wildcard which is used by the wx.FileDialog to filter out all the other files types except the JPEG format. Next, you create the wx.FileDialog. When you do that, you set its parent to None and give it a simple title. You also set the wildcard and the style. style is an open file dialog instead of a save file dialog.
Then you show your dialog modally. This means the dialog will appear over your main application and prevent you from interacting with it until you have accepted or dismissed the file dialog. If the user presses the OK button, then you will use GetPath() to get the path of the selected file and set the text control to that path. This effectively saves off the photo’s path so you can use it later.
Lastly, you call load_image() which will load the image into wxPython and attempt to show it. You can find out how by reading the following code:
The first step in this method is to extract the filepath from the text control widget. Then you pass that path along to a new instance of wx.Image. This will load the image into wxPython for you. Next, you get the width and height from the wx.Image object and use the max_size value to resize the image while maintaining its aspect ratio. You do this for two reasons. The first is because if you don’t, the image will get stretched out or warped. The second is that most images at full resolution won’t fit on-screen, so they need to be resized.
Once you have the new width and height, you Scale() the image down appropriately. Then you call your wx.StaticBitmap control’s SetBitmap() method to update it to the new image that you loaded. Finally, you call Refresh(), which will force the bitmap widget to redraw with the new image in it.
Here it is with a butterfly photo loaded in it:
Now you have a fully-functional application that can load JPEG photos. You can update the application to load other image types if you’d like. The wxPython toolkit uses Pillow, so it will load the same types of image file types that Pillow itself can load.
Wrapping Up
The wxPython toolkit is extremely versatile. It comes with many, many widgets built-in and also includes a wonderful demo package. The demo package will help you learn how to use the widgets in your own code. You are only limited by your imagination.
Play around with the code in this tutorial and then take a look at the demo package when you get a chance. Then start creating your own neat applications!









