There's more...
We previously created the Photo class. Now we can add some test code to our module to ensure that it functions as we expect. We can use the __name__ ="__main__" attribute
as before to detect whether the module has been run directly or not.
We can add the subsequent section of code at the end of the photohandler.py script to produce the following test application, which looks as follows:
Add the following code at the end of photohandler.py:
#Module test code def dispPreview(aPhoto): """Create a test GUI""" import tkinter as TK #Define the app window app = TK.Tk() app.title("Photo View Demo") #Define TK objects # create an empty canvas object the same size as the image canvas = TK.Canvas(app, width=previewsize[0], height=previewsize[1]) canvas.grid(row=0,rowspan=2) # Add list box to display the photo data #(including xyscroll bars) photoInfo=TK.Variable() lbPhotoInfo=TK.Listbox(app,listvariable=photoInfo, height=18,width=45, font=("monospace",10)) yscroll=TK.Scrollbar(command=lbPhotoInfo.yview, orient=TK.VERTICAL) xscroll=TK.Scrollbar(command=lbPhotoInfo.xview, orient=TK.HORIZONTAL) lbPhotoInfo.configure(xscrollcommand=xscroll.set, yscrollcommand=yscroll.set) lbPhotoInfo.grid(row=0,column=1,sticky=TK.N+TK.S) yscroll.grid(row=0,column=2,sticky=TK.N+TK.S) xscroll.grid(row=1,column=1,sticky=TK.N+TK.E+TK.W) # Generate the preview image preview_filename = aPhoto.previewPhoto() photoImg = TK.PhotoImage(file=preview_filename) # anchor image to NW corner canvas.create_image(0,0, anchor=TK.NW, image=photoImg) # Populate infoList with dates and exif data infoList=[] for key,value in aPhoto.filedates.items(): infoList.append(key.ljust(25) + value) if aPhoto.exifvalid: for key,value in aPhoto.exif_info.items(): infoList.append(key.ljust(25) + str(value)) # Set listvariable with the infoList photoInfo.set(tuple(infoList)) app.mainloop() def main(): """called only when run directly, allowing module testing""" import sys #Check the arguments if len(sys.argv) == ARG_LENGTH: print ("Command: %s" %(sys.argv)) #Create an instance of the Photo class viewPhoto = Photo(sys.argv[ARG_IMAGEFILE]) #Test the module by running a GUI if viewPhoto.filevalid==True: dispPreview(viewPhoto) else: print ("Usage: photohandler.py imagefile") if __name__=='__main__': main() #End
The previous test code will run the main() function, which takes the filename of a photo to use and creates a new Photo object called viewPhoto. If viewPhoto is opened successfully, we will call dispPreview() to display the image and its details.
The dispPreview() function creates four Tkinter widgets to be displayed: a Canvas to load the thumbnail image, a Listbox widget to display the photo information, and two scroll bars to control the Listbox. First, we create a Canvas widget the size of the thumbnail image (previewsize).
Next, we create photoInfo, which will be our listvariable parameter linked to the Listbox widget. Since Tkinter doesn't provide a ListVar() function to create a suitable item, we use the generic type TK.Variable() and then ensure we convert it to a tuple type before setting the value. The Listbox widget gets added; we need to make sure that the listvariable parameter is set to photoInfo and also set the font to monospace. This will allow us to line up our data values using spaces, as monospace is a fixed width font, so each character takes up the same width as any other.
We define the two scroll bars, linking them to the Listbox widget, by setting the Scrollbar command parameters for vertical and horizontal scroll bars to lbPhotoInfo.yview and lbPhotoInfo.xview. Then, we adjust the parameters of the Listbox using the following command:
lbPhotoInfo.configure(xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)
The configure command allows us to add or change the widget's parameters after it has been created, in this case linking the two scroll bars so the Listbox widget can also control them if the user scrolls within the list.
As before, we make use of the grid layout to ensure that the Listbox widget has the two scroll bars placed correctly next to it and the Canvas widget is to the left of the Listbox widget.
We now use the Photo object to create the preview.ppm thumbnail file (using the aPhoto.previewPhoto() function) and create a TK.PhotoImage object that can then be added to the Canvas widget with the following command:
canvas.create_image(0,0, anchor=TK.NW, image=photoImg)
Finally, we use the date information that the Photo class gathers and the EXIF information (ensuring it is valid first) to populate the Listbox widget. We do this by converting each item into a list of strings that are spaced out using .ljust(25)—it adds left justification to the name and pads it out to make the string 25 characters wide. Once we have the list, we convert it to a tuple type and set the listvariable (photoInfo) parameter.
As always, we call app.mainloop() to start monitoring for events to respond to.