본문 바로가기
James Cooper - Python Programming with D

Visual Programming of Tables of Data

by 자동매매 2023. 3. 23.

3
Visual Programming of Tables of
Data
In this chapter, we’ll look at a few ways to represent lists of data. We’ll start
by writing code to read in a list of states, along with some of their data.
Then we use these entries as examples for display, to easily find tables of all
the U.S. states and their capitals and population data. When writing this
book, we extracted the data from a table on Wikipedia and used Word to
convert them to a comma-separated list. Here’s how some of that data
looks:
Click here to view code image
Alabama, AL, 1819, Montgomery
Alaska, AK, 1960, Juneau
Arizona, AZ, 1912, Phoenix
Arkansas, AR, 1836, Little Rock
California, CA, 1850, Sacramento
The full file contains 50 lines of comma-separated data. You can either read
them in one at a time or just read them all into an array at once. Let’s use
the second approach:
Click here to view code image
class StateList():
def init (self, stateFile):

read all lines into the contents

with open(stateFile) as self.fobj:
self.contents = self.fobj.readlines()
The contents variable now contains an array of strings. We can parse them
one by one and create a state object for each one:
Click here to view code image
self.states = [] #create empty list
for st in self.contents:
if len(st)> 0 :
self.state = State(st) #create State object
self.states.append(self.state) #add to list
The rest of the work is done in the State class, which parses those strings,
breaking them apart at the commas and storing each in an internal variable:
Click here to view code image
class State():
def __init
(self, stateString):

split the string into tokens

self.tokens = stateString.split(",")
self.statename = "" # default if empty
if len(self._tokens) > 3 :
self._statename = self._tokens[ 0 ]
self._abbrev = self._tokens[ 1 ]
self._founded = self._tokens[ 2 ]
self._capital = self._tokens[ 3 ] # cap
Other than the accessor functions for the State variables, this is the bulk of
the program. In essence, the StateList class creates a list (array) of state
objects.
The rest of the program finds a way to display the data.
Creating a Listbox
The Listbox is much like ones you have seen in most applications: It is just
a list of strings that you can click on to select one. For our list of states, we
can quickly create such a Listbox like this:
Click here to view code image
class BuildUI():
def __init
(self, root, slist):
self.states= slist
self.listbox = Listbox(root, selectmode=SINGLE)
self.listbox.grid(column= 0 , row= 0 , rowspan= 4 , padx= 10 )
for state in self.states:
self.listbox.insert(END, state.getStateName())
This listbox is inserted in column 1 of a grid and spans several rows, as
shown. The slist variable contains the list of State objects. What we insert
in the listbox is just the name string from each state. The selection mode
can be SINGLE, BROWSE, MULTIPLE, or EXTENDED. The most
common choice is SINGLE. BROWSE enables you to move your selection
with the mouse. MULTIPLE allows you to select multiple elements. With
EXTENDED, you can select several groups using the Shift and Control
keys. Figure 3-1 shows the basic list for this example.
Figure 3-1 List of states in a Listbox, with the selectmode= SINGLE
Although the basic list does not contain a scrollbar, you can easily scroll
through the contents with the mouse. But it isn’t all that tricky to add in a
scrollbar as you can see in Figure 3-2.
Click here to view code image

set up the scroll bar

scrollBar = Scrollbar(root)

connect to listbox

scrollBar.config(command=self.listbox.yview)

stretch to top and bottom

scrollBar.grid(row= 0 , column= 1 , rowspan= 4 ,
sticky= "NS" )

connect scrollbar y movement to listbox

self.listbox.config(yscrollcommand=scrollBar.set)
Figure 3-2 illustrates your completed scrollbar.
Figure 3-2 Listbox with a scrollbar attached
Displaying the State Data
There are two things we need to add to display the data when you click on a
state name. First, we have to create a series of label fields along the right
side where the results will appear:
Click here to view code image

create 4 labels on right

self.lbstate = Label( "" )

one red one

self.lbabbrev = Label(root, text= "" ,
foreground= "red" )
self.lbcapital = Label( "" )
self.lbfounded = Label( "" )
self.lbstate.grid(column= 2 , row= 0 , sticky=W) #left
self.lbabbrev.grid(column= 2 , row= 1 , sticky=W)
self.lbcapital.grid(column= 2 , row= 2 , sticky=W)
self.lbfounded.grid(column= 2 , row= 3 , sticky=W)
Next, we have to intercept the click event from the Listbox and send it to a
callback function that can be activated when the click occurs. This takes
place within the BuildUI class, which avoids any awkward global variables.
Click here to view code image
self.listbox.bind('<>', self.lbselect)
You can bind any event to a callback function. The online Python
documentation lists all the events that can occur on each widget. Here we
bind the selection event to the lbselect method.
Then, the lbselect method simply finds the index of the list element you
clicked, looks up that state object, and pulls out these values. Finally, it
copies the state names into the label text (see Figure 3-3).
Click here to view code image
def lbselect(self, evt):
index = self.listbox.curselection() # tuple
i= int(index[ 0 ]) # this is the actual index
state = self.states[i] # get state from list
self.loadLabels(state)
def loadLabels(self,state):

fill in the labels from that state

self.lbstate.config(text=state.getStateName())
self.lbcapital.config(text=state.getCapital())
self.lbabbrev.config(text=state.getAbbrev())
self.lbfounded.config(text=state.getFounded())
Figure 3-3 Listbox and scrollbar, with details displayed for the selected
state
It might be nice to have a way to jump down in the alphabetical list without
having to scroll. If we add an entry field, we can use it to find the first state
that begins with that letter.
Click here to view code image
self.entry=Entry(root) # create entry field
self.entry.grid(column= 0 , row= 4 , pady= 4 )
self.entry.focus_set() # set the focus to it

bind a keypress to lbselect

self.entry.bind( "" , self.keyPress)
The keypress method fetches the character from the event field, converts it
to uppercase, and scans the state list for the first match. If it finds a match, it
sets the listbox index to that row, as Figure 3-4 shows.
Click here to view code image
def keyPress(self, evt):
char = evt.char.upper()
i= 0
found= False

search for state starting with char

while ( not found) and (i< len(self.states)):
found =self.states[i].getStateName().startswith(char)
if not found:
i = i+1
if found:
state = self.states[i] # get the state
self.listbox.select_clear( 0 , END) # clear
self.listbox.select_set(i) # set selection
self.listbox.see(i) # make visible
self.loadLabels(state) # load labels
Figure 3-4 Listbox and Entry field that lets you skip ahead to any
selected alphabetic position
Using a Combobox
The Combobox is a combination of an entry field and a drop-down list. You
can type into the entry field or select an entry from the list. In either case,
the method combo.get() returns the selected string.
Loading the Combobox is simpler than loading the listbox; you just pass it
an array of names:
Click here to view code image
names=[]
for s in self.states:
names.append(s.getStateName())
#add list to combo box
self.combo = Combobox(root, values=names)
self.combo.current( 0 )
self.combo.bind( '<>' ,
self.onselect)
self.combo.grid(column= 0 , row= 0 , rowspan= 8 , padx= 10 )
Clicking a state calls the onselect method, which loads the state data:
Click here to view code image
def onselect(self, evt):
index = self.combo.current()
state = self.states[index]
self.loadLabels(state)
If you set combo.current to 0 or a number greater than zero, that line in the
Combobox is selected, and that line is copied into the entry field. If you set
that value to None, then the entry box is empty (see Figure 3-5).
Figure 3-5 Combobox with current set to less than 0 (top) and equal to 0
(bottom)
The Treeview Widget
You can use the Treeview widget to view nested data or just use it to view
tables of data. It does a great job either way. For some reason, the Python
documentation on Treeview is pretty much impenetrable, though. The
simple summary in this section should get you up to speed more easily.
Header #0 Header Header Header
Labels
or text col
The Treeview table consists of a top row of headers followed by data rows.
The leftmost column, which is always named “#0”, can be labels for the
rows or can be a data column. Of course, there can also be rows that are
children of any row, which gives you the opportunity to build a tree. (We’ll
show you this in the section “Tree Nodes.”)
First, you need to create the columns for the state information:
Click here to view code image

create columns

tree["columns"] = ("abbrev", "capital", "founded")
tree.column("#0", width= 100 , minwidth= 100 ,
stretch=NO) # left column is always #0
tree.column("abbrev", width= 50 , minwidth= 50 ,
stretch=NO)
tree.column("capital", width= 100 , minwidth= 100 ,
stretch=NO)
tree.column("founded", width= 70 , minwidth= 60 ,
stretch=NO)
Note that we only create three named columns because the state names go
in the #0 column on the left. We define their widths and, most important, set
STRETCH=NO, which keeps the Treeview from widening columns that you
might prefer to keep narrow, such as the abbreviation column (this example
keeps it at 50 pixels wide).
Then you create the actual headings. Note that we named the columns
earlier; now we put headings in those named columns, using capitalized
header names:
Click here to view code image

create headings

tree.heading('#0', text='Name') # column 0 = names
tree.heading('abbrev', text='Abbrev')
tree.heading('capital', text='Capital')
tree.heading('founded', text='Founded')
Finally, we insert a couple of data rows:
Click here to view code image
tree.insert(node, rownum, text=col0txt, values=("one", "two",
"three"))
If you are inserting at the main row, the node can be blank. The text for
column 0 goes in text=. The remaining columns are in the list following
values=. Here is an example:
Click here to view code image
tree.insert("", 1, text="California", values=("CA",
"Sacramento", "1845"))
tree.insert("", 2, text="Kansas", values=( "KS",
"Topeka", "1845"))
Figure 3-6 shows the result.
Figure 3-6 Treeview with normal (top) and boldface (bottom) headers
It is pretty common to put the header row in boldface. You can do this with
a Style statement:
Click here to view code image
style = ttk.Style()
style.configure( "Treeview.Heading" ,
font=( None , 10 , "bold" ))
The trick in the font statement is that you set the size and bold, but you do
not change the current font. Figure 3-6 shows the result.
Inserting Tree Nodes
Suppose we actually want to create a tree instead of a table. We do this by
saving the node of that row and then inserting into that node:
Click here to view code image
folderCa= tree.insert("", 1, text="California",
values=( "CA", "Sacramento", "1845"))
tree.insert(folderCa, 3, text="",
values=(" ","pop=508,529"))
This gives you a little expandable mark alongside the California node (see
Figure 3-7).
Figure 3-7 Treeview with collapsed (top) and expanded (bottom) leaf
nodes
You can click that + sign to expand the tree. Note that we inserted under
column 2, the Capital column, which shows the population in Figure 3-7.
You can display the entire list of states using the States array we developed
in earlier examples; it is only four lines of code. Figure 3-8 shows the
window.
Click here to view code image
i= 1
for state in self.states:
self.tree.insert( "" , i,
text=state.getStateName(),
values=( state.getAbbrev(),
state.getCapital(),
state.getFounded()))
i += 1
Figure 3-8 States listed as a Treeview table
Moving On
Now that we’ve laid the groundwork, we can dig into the actual design
patterns which we begin discussing in Chapter 4, “What Are Design
Patterns?”
Example Code on GitHub
In all these samples, be sure to include the data file (States.txt) in the same
folder as the Python file. Also make sure all of these programs are part of
the project in Vscode or PyCharm.
States.txt: Data file for these list examples
SimpleList.py: Basic listbox
StateListScroll.py: Listbox with scrollbar
StateListBox.py: Listbox with entry field
StateDisplayCombo.py: Combobox display
TreeTest.py: Tree with expandable node
TreeStates.py: Tree of all states, capitals, and founding dates

'James Cooper - Python Programming with D' 카테고리의 다른 글

The Factory Pattern  (0) 2023.03.23
What Are Design Patterns?  (0) 2023.03.23
Visual Programming in Python  (0) 2023.03.23
Introduction  (0) 2023.03.23
Index  (0) 2023.03.23

댓글