I'm making a baseball program based on the strategy games produced by Avalon Hills from the 70s & 80s, and the last part is a gui. I've made all the code to run the game command line, and I've got the code to select lineups with a gui. I envision a 3by1 grid with a scoreboard on the first row, a text box displaying the result, out, home run, double play, etc., and the last row is divided between the pitcher and batter cards on the left side, and a frame of buttons. The frame will flip between an offence and defence frame. So first the defence frame comes up with options like pitching change, change position, and play ball. Play ball changes the frame to the offence options, which will be pinch hit, pinch run, steal, and so on. But how can I put the buttons inside a frame, then combine the player cards and buttons in another frame, and then add that to the main frame?
The DFrame & OFrame classes are inner classes (hence the "elf", not "self"). I've got the dynamic switching between the 2 Frames. My problem is breaking the DFrame main loop, it only plays the top of the first, and the self.roadOuts never increments. Here's what I've got:
while self.innings < 8.5 or self.homeScore == self.roadScore:
self.roadOuts = 0
while self.roadOuts < 3:
self.dFrame.mainloop()
class DFrame(Frame):
def __init__(elf, parent):
Frame.__init__(elf)
elf._playButton = Button(elf, text = 'Play Ball',
command = parent.oMenu)
elf._playButton.grid(row = 0, column = 0)
elf._pitchingButton = Button(elf, text = 'Pitching Change',
command = parent.pitchingChange)
elf._pitchingButton.grid(row = 1, column = 0)
elf._positionButton = Button(elf, text = 'Defensive Substitution',
command = parent.positionChange)
elf._positionButton.grid(row = 0, column = 1)
elf._alignButton = Button(elf, text = 'Change Positions',
command = parent.positionSwap)
elf._alignButton.grid(row = 1, column = 1)
elf._doubleButton = Button(elf, text = 'Double Switch',
command = parent.doubleSwitch)
elf._doubleButton.grid(row = 2, column = 0)
elf._walkButton = Button(elf, text = 'Intentional Walk',
command = parent.intentionalWalk)
elf._walkButton.grid(row = 2, column = 1)
elf._depthButton = Button(elf, text = 'Change Infield Depth',
command = parent.infieldDepth)
elf._depthButton.grid(row = 3, column = 0)
class OFrame(Frame):
def __init__(elf, parent):
Frame.__init__(elf)
elf._playButton = Button(elf, text = 'Play Ball',
command = parent.atBat)
elf._playButton.grid(row = 0, column = 0)
elf._pinchHitButton = Button(elf, text = 'Pinch Hit',
command = parent.pinchHit)
elf._pinchHitButton.grid(row = 1, column = 0)
elf._prfButton = Button(elf, text = 'Pinch Run First',
command = parent.pinchRunFirst)
elf._prfButton.grid(row = 0, column = 1)
elf._prsButton = Button(elf, text = 'Pinch Run Second',
command = parent.pinchRunSecond)
elf._prsButton.grid(row = 1, column = 1)
elf._prtButton = Button(elf, text = 'Pinch Run Third',
command = parent.pinchRunThird)
elf._prtButton.grid(row = 2, column = 1)
elf._stealButton = Button(elf, text = 'Steal',
command = parent.steal)
elf._stealButton.grid(row = 2, column = 0)
elf._bunt4HitButton = Button(elf, text = 'Bunt for a hit',
command = parent.bunt4AHit)
elf._bunt4HitButton.grid(row = 3, column = 0)
elf._hitNRunButton = Button(elf, text = 'Hit And Run',
command = parent.hitAndRun)
elf._hitNRunButton.grid(row = 4, column = 0)
elf._sacButton = Button(elf, text = 'Sacrifice',
command = parent.sacrifice)
elf._sacButton.grid(row = 4, column = 1)
elf._squeezeButton = Button(elf, text = 'Squeeze',
command = parent.squeeze)
elf._squeezeButton.grid(row = 3, column = 1)
the next method is called when the DFrame "play ball" button is clicked, and it makes the OFrame.
def oMenu(self):
self.dFrame.grid_forget()
self.dFrame.destroy()
self.oFrame = self.OFrame(self)
self.oFrame.grid(row = 1, column = 1)
self.oFrame.mainloop()
and at the end of an at bat, I have:
self.oFrame.grid_forget()
self.oFrame.destroy()
self.dFrame = self.DFrame(self)
self.dFrame.grid(row = 1, column = 1)
I'm not sure I understand your question. It seems like you know how to put one frame in another (It is really not much different than adding a Button to a frame -- or any other widget). I think what you're asking is how to dynamically switch which frame is being displayed at any given time.
You probably want the grid_forget method. using the play_ball button should cause the defense_frame to call it's grid_forget method, while re-gridding the the offense_frame. Of course, this would be pack_forget if you're using the pack geometry manager.
EDIT
Added a very rudimentary working example of the grid layout you described. It can probably be done a lot better, but this should get you started. (Particularly the switchOffenseDefense function and the switch_button button).
import Tkinter as tk
base=tk.Tk() #this is the main frame
root=tk.Frame(base) #Really this is not necessary -- the other widgets could be attached to "base", but I've added it to demonstrate putting a frame in a frame.
root.grid(row=0,column=0)
scoreboard=tk.Frame(root)
scoreboard.grid(row=0,column=0,columnspan=2)
###
#Code to add stuff to scoreboard ...
# e.g.
###
scorestuff=tk.Label(scoreboard,text="Here is the scoreboard")
scorestuff.grid(row=0,column=0)
#End scoreboard
#Start cards.
cards=tk.Frame(root)
cards.grid(row=1,column=0)
###
# Code to add pitcher and batter cards
###
clabel=tk.Label(cards,text="Stuff to add cards here")
clabel.grid(row=0,column=0)
#end cards
#Offense/Defense frames....
offense=tk.Frame(root)
offense.grid(row=1,column=1)
offense.isgridded=True #Dynamically add "isgridded" attribute.
offense_label=tk.Label(offense,text="Offense is coolest")
offense_label.grid(row=0,column=0)
defense=tk.Frame(root)
defense.isgridded=False
defense_label=tk.Label(defense,text="Defense is coolest")
defense_label.grid(row=0,column=0)
def switchOffenseDefense():
print "Called"
if(offense.isgridded):
offense.isgridded=False
offense.grid_forget()
defense.isgridded=True
defense.grid(row=1,column=1)
else:
defense.isgridded=False
defense.grid_forget()
offense.isgridded=True
offense.grid(row=1,column=1)
switch_button=tk.Button(root,text="Switch",command=switchOffenseDefense)
switch_button.grid(row=2,column=1)
root.mainloop()
Related
I have the following bits of code that creates a toplevel window and parses a dictionary into a Text widget:
def escrito(**kwargs):
write_window = Toplevel(root)
#write_window.title(kwargs) (problematic code)
writing_box = tk.Text(write_window, font = ("calibri", 20), width = 60, height = 15, wrap=WORD)
writing_box.pack(expand = tk.YES, fill = tk.X)
writing_box.grid(row = 0, column = 0, sticky = 'nswe')
texto = '\n'.join(key + ":\n" + value for key, value in kwargs.items())
writing_box.insert("1.0", texto)
def septic_osteo():
escrito(**infections.Septic_arthritis)
Septic_arthritis = {
'Empirical Treatment':
'Flucloxacillin 2g IV 6-hourly',
'If non-severe penicillin allergy':
'Ceftriaxone IV 2g ONCE daily',
'If severe penicillin allergy OR if known to be colonised with
MRSA':
'Vancomycin infusion IV, Refer to Vancomycin Prescribing
Policy',
'If systemic signs of sepsis': 'Discuss with Consultant
Microbiologist'
}
So when I run the code, the escrito functions parses the dictionary and writes its content onto a text widget contained on a Toplevel window. What I would like to know is how to dynamically rename the Toplevel window with the dicitonary's name. I do know that I can do this:
def septic_osteo():
escrito(**infections.Septic_arthritis)
write_window.title('Septic_arthritis)
but I do have like 100 functions like the one above, so, aside from labour intensive, I am not sure is the more pythonic way, so, is there a way that the window can be renamed with the dictionary name? (i.e. 'Septic_arthritis)
Thanks
If your data is in an object named infections, with attributes such as Septic_arthritis, the most straight-forward solution is to pass the data and the attribute as separate arguments, and then use getattr to get the data for the particular infection.
It would look something like this:
def escrito(data, infection):
write_window = Toplevel(root)
write_window.title(infection)
writing_box = tk.Text(write_window, font = ("calibri", 20), width = 60, height = 15, wrap="word")
writing_box.pack(expand = tk.YES, fill = tk.X)
writing_box.grid(row = 0, column = 0, sticky = 'nswe')
texto = '\n'.join(key + ":\n" + value for key, value in getattr(data, infection).items())
writing_box.insert("1.0", texto)
The important bit about the above code is that it uses getattr(data, infection) to get the data for the given infection.
If you want to create a button to call this function, it might look something like this:
button = tk.Button(..., command=lambda: escrito(infections, "Septic_arthritis"))
This will call the command escrito with two arguments: the object that contains all of the infections, and the key to the specific piece of information you want to display.
I need your help to figure out how to make the following coding:
My idea is to get a message if fields name and quantity are in blank at the time I execute a function through a button. I printed what is the value if name and quantity are in blank and program says it is .!entry and .!entry2 respectively.
Unfortunately it doesn't work. What I am doing wrong?
if name == .!entry and quantity == .!entry2:
message = Label(root, text = 'Name and Quantity required' , fg = 'red')
message.grid(row = 4, column = 0, columnspan = 2, sticky = W + E)
return
As #JordyvanDongen pointed out in the comments you should use entry.get() to get the text inside the the entry object.
I suggest this for you code, assuming the entry objects are named name and quantity:
if not name.get() or not quantity.get():
message = Label(root, text = 'Both name and quantity are required', fg = 'red')
message.grid(row = 4, column = 0, columnspan = 2, sticky = W + E)
return None
I changed the conditional to or so that if either of the fields are blank it will be triggered but you may want to change it back to and depending on your purposes..
I am writing a GUI application in Python using Tkinter. The application displays weather data for any given location and period of time chosen by the user. There are options for the user to display weather data for one week and one month. I am trying to display that data in a table, but I am getting the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Kevin\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "C:\Users\Kevin\Documents\Python\Weather Data Application\WeatherData.py", line 109, in getdata
for i, high, low, precip, conditions in enumerate(list, start = 1):
ValueError: not enough values to unpack (expected 5, got 2)
The table is supposed to have 5 columns. I am not sure why I am getting this error, as I have all the items I want to display in the list that I define. The part of the code that my error is occurring in is shown below:
#Display Data
for i in range(0,len(value)):
#Display data for one day
maxtemp = tk.Label(text = weather['locations'][place]['values'][i]['maxt'])
mintemp = tk.Label(text = weather['locations'][place]['values'][i]['mint'])
precipitation = tk.Label(text = weather['locations'][place]['values'][i]['precip'])
condition = tk.Label(text = weather['locations'][place]['values'][i]['conditions'])
if (radiovalue.get() == 2 and len(value) == 7) or radiovalue.get() == 3:
root.withdraw()
def goBack():
root.deiconify()
data.withdraw()
#new window
data = tk.Tk()
tablelabel = tk.Label(data, text = "Weather Data").grid(row = 0, columnspan = 5)
cols = ('Date', 'High Temperature', 'Low Temperature', 'Precipitation', 'Conditions')
weatherBox = ttk.Treeview(data, columns = cols, show = 'headings')
back = tk.Button(data, text = "Go Back", command = goBack).grid(row = 1)
for col in cols:
weatherBox.heading(col, text = col)
weatherBox.grid(row = 1, column = 0, columnspan = 5)
list = [weather['locations'][place]['values'][i]['maxt'],weather['locations'][place]['values'][i]['mint'],
weather['locations'][place]['values'][i]['precip'],weather['locations'][place]['values'][i]['conditions']]
for i, high, low, precip, conditions in enumerate(list, start = 1):
weatherBox.insert(values = (i, high, low, precip, conditions))
I'm making a small GUI application that deals with grades and whatnot and outputs the highest grades and etc.
Here's a part of the code:
root = Tk()
gradeList = []
def addGradeObject(name, percentage):
gradeList.append(Grade(name, percentage))
updateOutput()
print(gradeList)
def undoAdd():
try:
gradeList.pop(len(gradeList) - 1)
updateOutput()
print(gradeList)
except Exception:
pass
def updateOutput():
highestGrade.setText(highest_grade(gradeList))
lowestGrade.setText(lowest_grade(gradeList))
numFailed.setText(num_failed(gradeList))
addButton = Button(text = "Add Grade", command = lambda: addGradeObject (entryA.get(), entryB.get())).grid(row = 1, column = 4)
undoButton = Button(text = "Undo", command = undoAdd).grid(row = 2, column = 4)
entryA = Entry()
entryA.grid(row = 1, column = 1)
entryB = Entry()
entryB.grid(row = 1, column = 2)
highestGrade = Entry()
highestGrade.grid(row = 2, column = 1)
lowestGrade = Entry()
lowestGrade.grid(row = 3, column = 1)
numFailed = Entry()
numFailed.grid(row = 4, column = 1)
root.title("Grade Checker")
root.mainloop()
The problem is I'm getting this error:
AttributeError: 'Entry' object has no attribute 'setText'
I don't understand. When you create an Entry box, doesn't the object of class "Entry" have an attribute/method that allows you to set text to it? I really don't know why it's not working
Entry methods can be found here. As far as I can tell, there is no setText method. There is however an insert method that you could use to set the text (though you might wand to delete the current text first).
def set_text(entry, text):
entry.delete(0, tkinter.END)
entry.insert(0 text)
Alternatively, you can hook the entry up to a StringVar and use it's .set() and .get() methods. e.g. (from the linked docs above):
v = StringVar()
e = Entry(master, textvariable=v)
e.pack()
v.set("a default value")
s = v.get()
Here is a snippet
self.list_ctrl = wx.ListCtrl(self, size=(-1,100),
style=wx.LC_ICON|wx.LC_ALIGN_LEFT
)
il = wx.ImageList(16,16,True)
png = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,wx.ART_OTHER, (16,16))
il.Add(png)
self.list_ctrl.AssignImageList(il,wx.IMAGE_LIST_NORMAL)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
self.list_ctrl.InsertImageStringItem(0,"1",0)
self.list_ctrl.InsertImageStringItem(1,"2",0)
My problem is that the icons appear to the top of the text which should not happen because I put wx.LC_ALIGN_LEFT in the style. I would like the icons to appear left of the text.
Another problem is that, I want one element per row. In my code it is almost like one element per column.
Can anyone help me with any of these problems?
Thanks.
Looking at the wxPython demo for the ListCtrl, it looks like they use SetImageList() instead of AssignImageList(). Not sure what the difference is though. I don't see where you're inserting any text though. You'd need to use SetStringItem to put text in the other columns from what I can see.
EDIT: Code from wxPython Demo package, ListCtrl demo:
self.il = wx.ImageList(16, 16)
self.idx1 = self.il.Add(images.Smiles.GetBitmap())
self.sm_up = self.il.Add(images.SmallUpArrow.GetBitmap())
self.sm_dn = self.il.Add(images.SmallDnArrow.GetBitmap())
And then we add data / images to the widget
def PopulateList(self):
if 0:
# for normal, simple columns, you can add them like this:
self.list.InsertColumn(0, "Artist")
self.list.InsertColumn(1, "Title", wx.LIST_FORMAT_RIGHT)
self.list.InsertColumn(2, "Genre")
else:
# but since we want images on the column header we have to do it the hard way:
info = wx.ListItem()
info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
info.m_image = -1
info.m_format = 0
info.m_text = "Artist"
self.list.InsertColumnInfo(0, info)
info.m_format = wx.LIST_FORMAT_RIGHT
info.m_text = "Title"
self.list.InsertColumnInfo(1, info)
info.m_format = 0
info.m_text = "Genre"
self.list.InsertColumnInfo(2, info)
items = musicdata.items()
for key, data in items:
index = self.list.InsertImageStringItem(sys.maxint, data[0], self.idx1)
self.list.SetStringItem(index, 1, data[1])
self.list.SetStringItem(index, 2, data[2])
self.list.SetItemData(index, key)