Rec Team Maker

Label, LabelFrame, Frame, Entry, Button, Listbox, root, toplevel, Message ; plus using propagate()

Originally we though about trying to do just three examples covering all the widgets and this was one of them.  Turned out they would be too hard for new users to follow so we compromised – on about 15. The point is when you run it this looks pretty simple but there is a fair amount going on under the surface.  The fantasy here is you have a bunch of players to split into two equally matched teams. You can move players from one team to another, delete players entirely and add new players.  It starts with an intro screen, then when the user dismisses the intro screen it puts up a message that has to be acknowledged before you can play with the main application.

#Multiple Widget Demo: Label, LabelFrame, Frame, Entry, Button, Listbox, root, toplevel ; plus using propagate()
#This top section is just wikipython standard tkinter set up header code 2 
from tkinter import *
root = Tk()
root.attributes('-fullscreen', True)
root.configure(background='DarkSlategray4')
scrW = root.winfo_screenwidth()
scrH = root.winfo_screenheight()  
workwindow = str(1024) + "x" + str(768)+ "+" +str(int((scrW-1024)/2)) + "+" +str(int((scrH-768)/2))
top1 = Toplevel(root, bg="light blue")
top1.geometry(workwindow)
top1.title("Top 1 - Workwindow")
top1.attributes("-topmost", 1)  # make sure top1 is on top to start
root.update()                   # but don't leave it locked in place
top1.attributes("-topmost", 0)  # in case you use lower or lift
#exit button - note: uses grid
b3=Button(root, text="Egress", command=root.destroy)
b3.grid(row=0,column=0,ipadx=10, ipady=10, pady=5, padx=5, sticky = W+N)
#top1.grid_propagate(0)
#____________________________

# The purpose here is NOT code optimization but widget demonstration!
# One key thing nobody tells you - usual default is 1 character = 8 pixels
# and you will need to know this because frame widgets work on pixel widths
# whereas text based widgets (like labels) work on character widths and line heights

# VARIABLES AND STUFF
l1HeadTxt=StringVar()  #control varialbles - global in nature
l1HeadTxt.set("Team ONE List")
l2HeadTxt=StringVar()
l2HeadTxt.set("Team TWO List")
team1=["Andrea","Bob","Valerie","Charlie","Terry", "Meredith", "Sharon","Larry", "Vivion", "Donna"] #Teams would be stored on and retrieved
team2=["Dave", "Betty","Eric","Cathy","John","Liz", "Ben","Terri","Andrew", "Mike"]            #from disk in a real situation.
newEntry=StringVar()
newEntry.set("")
c1, c2, c3, c4, c5, c6="gray90","navy","red4","white","linen","dark green"  #makes it easy to change color scheme
player=StringVar()
player.set("")         # for demo I put relief around Frames and LabelFrames to revel the screen plan (done in Excel)
top1.configure(bg=c1)  # but in practice set master and frames all to the same background color
textOfMsg= '''           Welcome to multiple widget demo: REC TEAM MAKER
This little demo app includes examples of the following widgets:
Label,
LabelFrame,
Frame,
Entry,
Button,
Listbox,
root,
toplevel,
...and yours truly - Message.
Message is like a multi-line label with a few facilities for breaking
lines, but not really for managing text, etc.  For that, you will need to use
a Text widget.  And don't confuse me with a Messagebox - you won't find that trashy, communicative cousin here. Messagebox is in a different demo.
    Now, when you are ready, click the button below that says "Move On" and, alas, I will be gone for good.
                                        Have a nice day.'''


# SET UP LABELFRAME CLASS TO BUILD 7 FRAMES ACROSS THE TOP
class myLabelFrame(LabelFrame):   #Using LabelFrames to force define our design
    def __init__(self,width,height,relief,background,row,column):
        global selfhold       
        selfhold=self
        self.width=width
        self.height=height
        self.relief=relief
        self.background=background        
        self=LabelFrame(top1,width=self.width,height=self.height,relief=self.relief,bg=self.background)
        self.propagate(0)
        if column==1:  #make a side trip to stuff in a couple of column headers
            Label(self, text=l1HeadTxt.get(), font="Arial 12 bold", bg=c2, fg=c4).pack()
        if column==5:
            Label(self, text=l2HeadTxt.get(), font="Georgia 13 bold", bg=c3, fg=c4).pack(expand=True, fill=BOTH)     
        self.grid(row=row, column=column) #the key to propagate, don't grid the container until
                 # the labels are packed into it.

# FUNCTIONS AND STUFF  - these are the "verbs" of Python as opposed to the "nouns" of class definitions
                 
def t1DelSel():            # these should all be pretty self explanatory - tried to pick names that 
    Team1.delete(ANCHOR)   # kind of explain function - Team 1 Delete Selection = t1DelSel

def t2DelSel():
    Team2.delete(ANCHOR)

def bl1tol2():             # button to transfer from List 1 to List 2 = bl1tol2
    player=Team1.get(ACTIVE)
    player2move=Team1.curselection()
    Team2.insert(END, player)
    Team1.delete(ACTIVE)

def bl2tol1():
    player=Team2.get(ACTIVE)
    player2move=Team2.curselection()
    Team1.insert(END, player)
    Team2.delete(ACTIVE)

def e2T1():                 # e is for entry; e2T1= move entry value to Team 1, then clear entry
    player=e.get()
    if player=="":
        player="Unknown"
    Team1.insert(END, player)
    e.delete(0,END)        #all caps usually indicates a dedicated constant defined in Python/tkinter
    
def e2T2():
    player=e.get()
    if player=="":
        player="Unknown"    
    Team2.insert(END, player)
    e.delete(0,END)

# CREATE AND POST THE WIDGETS IN OUR SCREEN PLAN

# Set up the columns and list headers       
colwidths=[50,287,150,50,150,287,50]  # would have put these up with the variable but
headColors=[c1,c2,c1,c1,c1,c2,c1]   # it is easier to see and understand having them here
row1, lfheight, relief = 0, 40, GROOVE  #lf stands for "LabelFrame" - to finish chg groove to flat
for colcount in range(7):  # ...and just like that, our columns are set up
    myLabelFrame(colwidths[colcount],lfheight, relief, headColors[colcount], 0, colcount)

# Put a tittle header in     NOTE - this demos you can grid one widget on top of another
title=Label(top1, text="Rec Team Maker", bg=c6, fg=c1, font='arial 11 bold')
title.grid(row=0, column=2, columnspan=3, sticky=E+W)

# Put in sidebars and middle bar        ...and boom - our row structure is set
lfLeft1= LabelFrame(top1, width=50, bg=c1, height=230).grid(row=1, column=0, rowspan=1, sticky=N)
lfLeft2= LabelFrame(top1, width=50, bg=c1, height=230).grid(row=2, column=0, rowspan=1, sticky=N)
lfLeft3= LabelFrame(top1, width=50, bg=c1, height=268).grid(row=3, column=0, rowspan=2, sticky=N)
lfRight= LabelFrame(top1,width=50, bg=c1, height=728).grid(row=1, column=6, rowspan=4, sticky=N)
lfMid = LabelFrame(top1, width=50, bg=c1, height=460).grid(row=1, column=3, rowspan=2, sticky=N)

# Team1 (leftside) listbox and delete button
T1frame=Frame(top1,height=690, width=287, relief=SUNKEN, bd=8) 
T1frame.grid_propagate(0)
Team1=Listbox(T1frame, relief=FLAT, width=286, height=33, font="Verdana 14", bg=c5)
Team1.grid(row=0, column=0)
for player in team1:
   Team1.insert(END, player)
T1frame.grid(row=1, column=1, rowspan=3)

Bt1Del=Button(top1, width=35, text="Remove Selected Player", bg=c2, fg=c4, command=t1DelSel)
Bt1Del.grid( row=4, column=1)

# Team2 (rightside) listbox and delete button
T2frame=Frame(top1, height=690, width=287, relief=SUNKEN, bd=8)
T2frame.grid_propagate(0)
Team2=Listbox(T2frame, relief=FLAT, width=286, height=33, font="Georgia 15",bg=c5)
Team2.grid(row=0, column=0)
for player in team2:
   Team2.insert(END, player)
T2frame.grid(row=1, column=5, rowspan=3)

Bt2Del=Button(top1, width=35, text="Remove Selected Player", bg=c3, fg=c4, command=t2DelSel)
Bt2Del.grid( row=4, column=5, rowspan=3)

# Button to move selection Team 1 to Team2 stuffed in frame for sizing
selFmLfTop=LabelFrame(top1, relief=GROOVE, width=150, height=230, bg=c1, bd=5)
selFmLfTop.pack_propagate(0)
B1to2=Button(selFmLfTop, width=15, height=3, bg=c2, fg=c4, command=bl1tol2, text="Move Selection\nto Team 2 ->", relief=RAISED, bd=5)
B1to2.pack()
selFmLfTop.grid(row=1, column=2, sticky=S)

# Button to send a new player to a team
selFmLfBtm=LabelFrame(top1, relief=GROOVE, width=150, height=230, bg=c1, bd=5)
selFmLfBtm.pack_propagate(0)
NewT1Item=Button(selFmLfBtm, width=15, height=3, bg=c2, fg=c4, command=e2T1, text="Move New\n Player\nto Team 1", relief=RAISED, bd=5)
NewT1Item.pack(side="bottom")
selFmLfBtm.grid(row=2, column=2, sticky=N)

# Button to move selection Team 2 to Team1 stuffed in frame for sizing
selFmRtTop=LabelFrame(top1, relief=GROOVE, width=150, height=230, bg=c1, bd=5)
selFmRtTop.pack_propagate(0)
B2to1=Button(selFmRtTop, width=15, height=3, bg=c3, fg=c4, command=bl2tol1, text="Move Selection\n<-  to Team 2", relief=RAISED, bd=5)
B2to1.pack()
selFmRtTop.grid(row=1, column=4, sticky=S)

# Button to send a new player to a team
selFmRtBtm=LabelFrame(top1, relief=GROOVE, width=150, height=230, bg=c1, bd=5)
selFmRtBtm.pack_propagate(0)
NewT2Item=Button(selFmRtBtm, width=15, height=3, bg=c3, fg=c4, command=e2T2, text="Move New\n Player\nto Team 2", relief=RAISED, bd=5)
NewT2Item.pack(side="bottom")
selFmRtBtm.grid(row=2, column=4, sticky=N)

# Create Frame for entry label and entry widget
eframe=Frame(top1, relief=GROOVE, width=351, height=230, bg=c1, bd=5) #Frame or LabelFrame - either one works here
eframe.grid_propagate(0)
eframe.grid(row=3, column=2, columnspan=3, stick=E+W)

elabel=Label(eframe, width=30, height=3, text= "ENTER NEW PLAYER NAME BELOW", bg=c6, fg=c4, font="arial 11 bold", anchor="w")
elabel.grid(row=0, column=0, sticky=E+W)  #undersized the width a little in fine tuning - looks better in relief

e=Entry(eframe, bd=7, cursor="man", font="Georgia 11 bold", relief=RIDGE, bg="blanched almond")
e.focus_set()
e.grid(row=1,column=0, sticky=W+E)

# Build and deploy screens 2 and 3
# This is a message to explain a feature of the form
top3=Toplevel(root, bg='black')
top3.attributes("-topmost", 0)
top3.geometry(str(500) + "x" + str(400)+ "+" +str(int((scrW-500)/2)) + "+" +str(int((scrH-400)/2)))
top3.lower(belowThis=None)

# This is the opening message of the app              
top2= Toplevel(root, bg="burlywood2")
top2.geometry(workwindow)
top2.attributes("-topmost", 1)

# Have to put these functions in place before they are needed
def top3Gone(self):
    top1.attributes("-topmost", 1)
    e.focus_set()
    top3.destroy()
    root.update()
  
def notice():
    top2.attributes("-topmost", 0)    
    top3.lift(aboveThis=None)
    t3lab.focus_set()
    top2.destroy()
    root.update()
    
# Intro Screen - top2 - continue
msg=Message(top2, bg="wheat1", text=textOfMsg, relief=GROOVE, font="Georgia 14")
msg.pack(padx=50, pady=50)

B1= Button(top2, text= "Be Gone 'o Message\nand reveal thy true purpose.", height=2, command=notice)
B1.pack(pady=20)
B1.focus_set()

# Popup message with text gets focus when opening screen is destroyed

t3lab= Label(top3, height=6, text="If no player is selected, \n the first player is \nmoved to other team.\n\nPress any key to continue.")
t3lab.pack(pady=10)
t3lab.bind("<Key>", top3Gone)
t3lab.bind("<1>", top3Gone)
top3.focus_set

#____________________________
root.mainloop()