This article is part four in our ongoing Python tkinter tutorial series. When we left off in part three, we had just completed configuring the inputs for our interface, which included text entry, label and button widgets. We'll now move on to the widgets necessary for conveying the application's two outputs: 1) the hash digest value of the file input supplied by the user, and 2) a message indicating whether the file hash generated by the app matches the optional hash value that may be supplied by the user.
In our mockup design for the application's interface, the output-related objects are comprised of four different widgets, all of which are basically just labels: 1) a pair for the output value of the digest that results from hashing the input file, and 2) a pair indicating whether that hash digest matches the optional input value that may be supplied by the user. Since the output values are going to be dynamically generated, we're also going to need two tkinter string variable objects to grab and display the dynamic values.
However, we have to be a bit careful here. If you run your app as-is, where we left off in the previous article, you'll notice that you are not able to copy the text from any of the labels we've already configured for the app. This is simply not possible because of the nature of Python tkinter labels. But it would obviously be nice if users were able to copy the output digest of the hash function. Indeed, most users would probably expect they would be able to do so, and would be annoyed if they couldn't. For this reason, we are going to use an Entry widget styled to look like a label for the hash value output, since you can copy text from an Entry widget.
To configure our output widgets, we're going to follow much the same method as we used for our inputs. We'll create an outputs frame embedded in the main frame, and then drop the various output widgets in the output frame's grid. Following our usual process, let's create a method to configure our output widgets, configure a new row to hold the outputs frame in the main frame, and call the configure outputs method from our setup method. Here is the relevant code from our View class:
This should all look pretty familiar by now. Notice, we have embedded the outputs frame at row 2, column 0 in the main frame since the inputs frame is in row 1 column 0 and the banner is in row 0 column 0. We've also given it some basic styling so we know where it is, and made it sticky in all four directions.
Our outputs frame is going to have two columns (one for a label indicating the name of the output, and one for displaying the output itself) and two rows (one for the hash digest output, and one for the result message indicating whether there is a match). Let's define them now, giving each a weight of 1.
Taking a stab at the top row of outputs, we'll need: 1) a label to indicate to the user that she is looking at the output hash digest value, 2) a string variable to hold the hash digest value, and 3) an entry widget to provide a selectable display of the value.
In the snippet above, we've configured a simple label indicating the result, and put it in the top left cell of the output frame grid (row 0, column 0), and provided it with the text 'Hash Value:'. We've also made it sticky to the east, so the text is displayed just to the left of the entry field. Next, we've created a string variable to hold the hash value itself, and provided it with a default text string. Notice that we've assigned this string variable to a class instance attribute because we are going to have to access it from other methods so we can get it and set it dynamically. Finally, we've configured an Entry widget to be 'readonly', with a flat relief and no highlight thickness. This latter configuration ensures that its displayed text will look like that of a label (at least on my system!). We've embedded it at row 0, column 1 in the outputs frame grid, and made it sticky to the west so it hugs its corresponding indicator label. We've further made the Entry object a class instance attribute so we can access it from other class methods as necessary. (Note: you may have to toggle the state of this Entry widget from 'readonly' to 'normal' when you want to insert a new text variable string. I have seen inconsistent information in this regard, but this toggle action was not necessary on my system.)
Now we'll move on to our second row, which will hold two labels: one indicating that that we are looking at the result of the matching function, and one displaying the result itself (i.e. if there was a match between the user-supplied hash value and the actual hash value of the user-supplied file or not). We'll also need a string variable to hold the desired result message.
The code above is pretty straightforward, and very similar to what we did for the previous row. We have a label indicating that we're dealing with the result, embedded it in row 1, column 0, and made it sticky to the east. Next we set a string variable as an instance attribute because we need to be able to set it from other methods, and provided it with a default text string. Finally, we have a third label that we are going to set dynamically with the appropriate text indicating the outcome of the hash check.
If you run the app now and play around with it a bit, you'll notice you are able to copy the string variable text associated with the 'Hash Value:' label, but that you cannot copy the string variable text associated with the 'Result:' label, just as we desired. Here's the current state of the interface with background highlighting of the three basic frames we've constructed: the banner (blue), the inputs (white), the outputs (green):
Python tkinter tutorial at the link. You can find the next article in our tkinter tutorial series (part 5) at the link.