• No results found

Add GUI elements with EasyGUI

In document Real Python Part 1 (Page 182-190)

We’ve made a few pretty pictures with matplotlib and manipulated some files, but otherwise we have limited ourselves to programs that are generally invisible and occasionally spit out text. While this might be good enough for most purposes, there are some programs that could really benefit from letting the user “point and click” - say, a script you wrote to rename a folder’s worth of files that your technologically impaired friend now wants to use. For this, we need to design a graphical user interface (referred to as a GUI and pronounced “gooey” - really).

When we talk about GUIs, we usually mean a full GUI application where everything about the pro-gram happens inside a window full of visual elements (as opposed to a text-based propro-gram). De-signing good GUI applications can be incredibly difficult because there are so many moving parts to manage; all the pieces of an application constantly have to be listening to each other and to the user, and you have to keep updating all the visual elements so that the user only sees the most recent version of everything.

Instead of diving right into the complicated world of making GUI applications, let’s first add some individual GUI elements to our code. That way, we can still improve the experience for the person using our program without having to spend endless hours designing and coding it.

We’ll start out in GUI programming with a module named EasyGUI. First, you’ll need to download this package:

Windows: If you have easy_install set up, then you can install the package by typing “easy_install easygui” at a command prompt. Otherwise,downloadthe compressed .zip file, unzip it, and install the package by running Python on setup.py from the command prompt as described in the installa-tion secinstalla-tion.

OS X: If you have easy_install set up, then you can install the package by typing “sudo easy_install easygui” into the Terminal. Otherwise, downloadthe compressed .tar.gz file, decompress it, then install the package by running Python on setup.py from Terminal as described in the installation section.

Debian/Linux:sudo apt-get install python-easygui

EasyGUI is different from other GUI modules because it doesn’t rely on events. Most GUI applica-tions are event-driven, meaning that the flow of the program depends on acapplica-tions taken by the user;

this is usually what makes GUI programming so complicated, because any object that might change in response to the user has to “listen” for different “events” to occur. By contrast, EasyGUI is struc-tured linearly like any function; at some point in our code, we display some visual element on the screen, use it to take input from the user, then return that user’s input to our code and proceed as usual.

Let’s start by importing the functionality from EasyGUI into the interactive window and displaying a simple message box:

1 >>> from easygui import *

2 >>> msgbox("Hello, EasyGUI!", "This is a message box", "Hi there")

'Hi there' >>>

When you run the second line of code, something like the window above should have appeared.

On Mac, it will look more like this:

And in Ubuntu:

If nothing appears, see the box below. For the sake of the majority, I’ll be sticking to screenshots from a Windows GUI perspective only.

We used the msgbox() to generate a new box that includes a message, a window title, and button text that we provided, in that order. (The default value for the button text would be “OK” if we hadn’t provided a third argument.) When the user clicks the button, EasyGUI returns the value of the button’s text, and we’re back to the interactive prompt.

NOTE: EasyGUI uses a toolkit called Tkinter, which we will get to use in the next sec-tion. IDLE uses Tkinter to run as well. You might run into problems with freezing win-dows, etc., because of disagreements between the new windows you create and the IDLE window itself. If you think this might be happening, you can always try running your code or script by running Python from the command prompt (Windows) or Terminal (Mac/Linux).

Figure 16.1: message_box_mac

Figure 16.2: message_box_ubuntu

Notice that when we ran the previous code, clicking the button returned the value that was in the button text back to the interactive window. In this case, returning the text of the button clicked by the user wasn’t that informative, but we can also provide more than one choice by using a buttonbox() and providing a tuple of button values:

1 >>> choices = ("Blue", "Yellow", "Auuugh!")

2 >>> buttonbox("What is your favorite color?", "Choose wisely...", choices)

'Auuugh!' >>>

Now we were able to tell that our user chose “Auuugh!” as the favorite color, and we could set that return value equal to a variable to be used later in our code.

There are a number of other ways to receive input from the user through easyGUI to suit your needs.

For starters, try out the following lines a few times each and see what values they return based on the different choices you select:

1 >>> choices = ("Blue", "Yellow", "Auuugh!") # tuple of choices

2

3 >>> title = "Choose wisely..." # window title

4

5 >>> indexbox("What is your favorite color?", title, choices)

6

7 >>> choicebox("What is your favorite color?", title, choices)

8

9 >>> multchoicebox("What are your favorite colors?", title, choices)

10

11 >>> enterbox("What is your favorite color?", title)

12

13 >>> passwordbox("What is your favorite color? (I won't tell.)", title)

14

15 >>> textbox("Please describe your favorite color:")

Another useful feature provided by EasyGUI is a simple way for the user to select a file through a standard file dialog box:

1 >>> fileopenbox("message", "title", "*.txt")

Figure 16.3: open_dialog

The third argument we passed to fileopenbox() was the type of file we want the user to open, which we specify in a string by using a “wildcard” * symbol followed by the name of the file extension. This automatically filters the viewable files to those that match the “.txt” extension, although the user still has the option to change this box to “All files (.)” and select any type of file.

Notice how we still provided a “message” and a “title” even though they both appeared in the title bar; typically you will just want to pass an empty string to one of these, since a single title is enough.

If we look up the easyGUI documentation to find out the names of the variables used by the function fileopenbox(), we can also provide only specific arguments by directly assigning values to the variable names, like so:

1 >>> fileopenbox(title="Open a file...", default="*.txt")

Within a “file open” dialog box, try typing in the name of a file that doesn’t exist and opening it;

the dialog box won’t let you! This is one less thing that we have to worry about programming into our code. The user can still hit cancel, in which case None is returned to represent a lack of any object. (See the call-out box at the very end of section 9.1 for a more complete explanation of the None keyword.) Otherwise, fileopenbox() gives us a string representing the full path to the selected file. Keep in mind that we aren’t actually opening this file in any way; we’re simply presenting the user with the ability to select a file for opening, but the rest of the code is still up to us.

There is also a diropenbox() function for letting the user choose a folder rather than an individual file; in this case, the optional third argument can tell the dialog box which directory to have open by default.

Finally, there is a filesavebox() that works similarly to fileopenbox() except that it allows the user to select a file to be “saved” rather than opened. This dialog box also confirms that the user wants to overwrite the file if the chosen name is the same as a file that already exists. Again, no actual saving of files is happening - that’s still up to us to program once we receive the file name from easyGUI.

In practice, one of the most difficult problems when it comes to letting the user select files is what to do if the user cancels out of the window when you need to have selected a file. One simple solution is to display the dialog in a loop until the user finally does select a file, but that’s not very nice - after all, maybe your user had a change of heart and really doesn’t want to run whatever code comes next.

Instead, you should plan to handle rejection gracefully. Depending on what exactly you’re asking of the user, most of the time you should use exit() to end the program without a fuss when the user cancels. (If you’re running the script in IDLE, exit() will also close the current interactive window.

It’s very thorough.)

Let’s get some practice with how to handle file dialogs by writing a simple, usable program. We will guide the user (with GUI elements) through the process of opening a PDF file, rotating its pages in some way, and then saving the rotated file as a new PDF:

1 from easygui import *

2

3 from pyPdf import PdfFileReader, PdfFileWriter

4

5 # let the user choose an input file

6

7 inputFileName = fileopenbox("", "Select a PDF to rotate...", "*.pdf")

8

9 if inputFileName == None: # exit on "Cancel"

10

11 exit()

12

13 # let the user choose an amount of rotation

14

15 rotateChoices = (90, 180, 270)

16

17 message = "Rotate the PDF clockwise by how many degrees?"

18

19 degrees = buttonbox(message, "Choose rotation...", rotateChoices)

20

21 # let the user choose an output file

22

23 outputFileName = filesavebox("", "Save the rotated PDF as...", "*.pdf")

24

25 while inputFileName == outputFileName: # cannot use same file as input

26

27 msgbox("Cannot overwrite original file!", "Please choose another file...")

28

29 outputFileName = filesavebox("", "Save the rotated PDF as...", "*.pdf")

30

31 if outputFileName == None:

32

33 exit() # exit on "Cancel"

34

35 # read in file, perform rotation and save out new file

36

37 inputFile = PdfFileReader(file(inputFileName, "rb"))

38

39 outputPDF = PdfFileWriter()

40

41 for pageNum in range(0, inputFile.getNumPages()):

42

Besides the use ofexit(), you should already be familiar with all the code in this script. The tricky part is the logic - i.e., how to put all the different pieces together to create a seamless experience for the user. For longer programs, it can be helpful to draw out diagrams by hand using boxes and arrows to represent how we want the user to experience the different parts of our program; it’s a good idea to do this even before you start writing any code at all. Let’s represent how this script works in an outline form in terms of what we display to the user:

1. Let the user select an input file

2. If the user canceled the “open file” dialog (None was returned), exit the program 3. Let the user select a rotation amount

• [No alternative choice here; the user must click a button]

4. Let the user select an output file

5. If the user tries to save a file with the same name as the input file:

• Alert the user of the problem with a message box

• Return to step 4

6. If the user canceled the “save file” dialog (None was returned), exit the program

The final steps are the hardest to plan out. After step 4, since we already know (from step 2) that the input file isn’t None, we can check whether the output file and input file match before checking for the canceled dialog. Then, based on the return value from the dialog, we can check for whether or not the user canceled the dialog box after the fact.

Review exercises:

1. Recreate the three different GUI elements pictured in this section by writing your own scripts without referring to the provided code

2. Save each of the values returned from these GUI elements into new variables, then print each of them

3. Test out indexbox(), choicebox(), multchoicebox(), enterbox(), passwordbox() and textbox() to see what GUI elements they produce; you can use the help() function to read more about each function in the interactive window - for instance, type import easygui then help(easygui.indexbox)

In document Real Python Part 1 (Page 182-190)