In this post I’ll demonstrate how to build a object oriented Tkinter GUI application for associating labels to filenames in order to quickly and easily build a set of training data. The Submit button will associate the label with the file, and the Save and Quit button will dump the file and its associated label into a Python dict, and then a cPickle file for later use. This is still a little rough around the edges; it assumes that you’re looking for PNG data in the current directory, and the output overwrites previous output, but it’s a start.
Important Resources
I wold not have been able to do this without the help of zetcode for simple, complete, object-oriented examples, and effbot for detailed information about widgets and other best practices.
Code
These are my imports: Tkinter
for the GUI, PIL.Image
and ImageTk
for handling PNG images, glob
for finding the PNG images in the working directory, and cPickle
for storing the output.
from Tkinter import * import PIL.Image, ImageTk, cPickle, glob
This is a pretty simple, one-level GUI. We have an entry box, and then two buttons. This is set up so that you can enter data on the keypad and then hit the keypad Enter key to go to the next data. When you’re done, the application produces a cPickle file for later use.
class App( Frame ): def __init__( self, parent ): Frame.__init__( self, parent ) self.parent = parent self.parent.grid() self.train = dict() self.i = 0 self.fns = glob.glob("*.png") ## start up the UI self.initUI() def initUI( self ): ## set the key bindings for the Enter key, and the keypad Enter key self.parent.bind( '<Return>', self.submit_callback ) self.parent.bind( '<KP_Enter>', self.submit_callback ) ## name of the program self.parent.title( "Label Training Data" ) ## open the image image = PIL.Image.open( self.fns[ self.i ] ) photo = ImageTk.PhotoImage( image, master=self ) ## put the image in a Label object img_label = Label( self, image=photo ) img_label.image = photo img_label.grid(row=0,columnspan=2) ## entry ent_label = Label( self, text="Label:" ) ent_label.grid( row=1, column=0 ) self.entry = Entry( self ) self.entry.grid( row=1, column=1 ) self.entry.focus() ## submit button submit_btn = Button( self, text="Submit" ) submit_btn.bind( '<Button-1>', self.submit_callback ) submit_btn.grid( row=2, columnspan=2 ) ## quit button quit_btn = Button( self, text="Save and Quit", command=self.quit_callback ) quit_btn.grid( row=3, columnspan=2 ) ## pack it up self.pack() def submit_callback( self, event=None ): ## associate the user input with the filename self.train[ self.fns[ self.i ] ] = self.entry.get() ## increment the counter self.i += 1 ## if we're at the end of the data.. if self,i == len( self.fns ): ## save/dump the data cPickle.dump( self.train, open( "train_complete.pkl", "w" ), -1 ) ## kill the aplication self.parent.destroy() ## reload the window self.initUI() def quit_callback( self ): ## save/dump the data cPickle.dump( self.train, open( "train_partial.pkl", "w" ), -1 ) ## kill the aplication self.parent.destroy()
Now that the application is written out, we execute it as Pythonically as Pythonistically possible,
def main(): root = Tk() root.geometry("250x100+100+100") app = App( root ) root.mainloop() if __name__=="__main__": main()
We can then extract our data by opening the cPickle output.
train = cPickle.load( open( "train.pkl", "r" ) ) for k, v in train.iteritems(): print '{:>20}{:>10}'.format(k,v)