I'm new in Python and I'm currently trying to use tkinter as first GUI. I was used to making it without classes. And it is my first time to use import tkinter as tk instead of import *
import tkinter as tk
def update():
pass
#Game.statsFrame #doesn't work Game.statsFrame.stat1_amountLabel too
#Game.stat1_amountLabel #doesnt work < want to use update_idletasks() or
#just type new cofnig...
#just errors like: "Game' has no attribute 'statsFrame" etc #Game
class character:
name = ""
experience = 0
level = 0
gold = 0
stat1 = 0
stat2 = 0
stat3 = 0
stat4 = 0
stat5 = 0
avaiblePoints = 0
def add_stat1(self):
if self.avaiblePoints >= 1:
self.stat1 += 1
self.avaiblePoints -= 1
update()
else:
pass
def add_stat2(self):
if self.avaiblePoints >= 1:
self.stat2 += 1
self.avaiblePoints -= 1
update()
[...]
myChar = character()
myChar.avaiblePoints = 3
class Game:
def __init__(self, parent):
self.myParent = parent
self.myGame = tk.Frame(parent)
self.myGame.grid()
self.statsFrame = tk.Frame(self.myGame).grid()
self.stat1Label = tk.Label(self.statsFrame)
self.stat1Label.config(text="Strength:")
self.stat1Label.grid(column=1, row=1)
self.stat1_amountLabel = tk.Label(self.statsFrame)
self.stat1_amountLabel.config(text=myChar.stat1)
self.stat1_amountLabel.grid(column=2, row=1)
self.add_stat1Button = tk.Button(self.statsFrame)
self.add_stat1Button.config(text="+", command=myChar.add_stat1)
self.add_stat1Button.grid(column=3, row=1)
root = tk.Tk()
myapp = Game(root)
root.mainloop()
But I can't get to (for example) stat1Label and change text inside it and after it use update_idletasks(). It's like it doesnt exist. Errors shows that Game has not atributtes like stat1Label etc.
I want to use it becouse I have read that __init__ method is better and I want to swtich between pages. I have no idea, when I wasn't using class in tkinter some things (like this) was easier and had no problems. I'm very confused guys.
It's excellent that you're using import tkinter as tk instead of the dreaded "star" import, and that you're trying to organize your code with classes. It can be a little confusing at first, but it makes your code more modular, which helps enormously, especially when the GUI gets large.
There are a few problems with your code. The most important one is this line:
self.statsFrame = tk.Frame(self.myGame).grid()
The .grid method (and .pack and .place) all return None. So that line saves None to self.statsFrame, not the Frame widget. So when you later try to do stuff with self.statsFrame it won't do what you expect.
Another problem is that the text attribute of your self.stat1_amountLabel doesn't track the value of myChar.stat1, so when you change the value of myChar.stat1 you need to explicitly update the Label with the new value. Alternatively, you could use the textvariable attribute with an IntVar to hold the character's stat. See the entry for textvariable in the Label config docs for info.
Your character class has a whole bunch of attributes like name, experience etc as class attributes. That's not a good idea because class attributes are shared by all instances of a class. But you probably want each character instance to have their own instance attributes. So you should give character an __init__ method where you set those attributes. OTOH, it's ok to use class attributes for default values that get overridden by instance attributes.
Anyway, here's a repaired version of your code with a Button that updates the Strength stat. I've put the stats in a list, rather than having a bunch of separate named stats that would have to be managed separately. And I've given Game a make_stat method so you can easily add rows for the other stats.
import tkinter as tk
class Character:
def __init__(self, availablePoints=0):
self.name = ""
self.experience = 0
self.level = 0
self.gold = 0
self.stats = [0] * 5
self.availablePoints = availablePoints
def add_stat(self, idx):
if self.availablePoints >= 1:
self.stats[idx] += 1
self.availablePoints -= 1
class Game:
def __init__(self, parent):
self.myParent = parent
self.myGame = tk.Frame(parent)
self.myGame.grid()
self.statsFrame = tk.Frame(self.myGame)
self.statsFrame.grid()
self.make_stat("Strength:", 0, 0)
def make_stat(self, text, idx, row):
label = tk.Label(self.statsFrame, text=text)
label.grid(column=1, row=row)
amount = tk.Label(self.statsFrame, text=myChar.stats[idx])
amount.grid(column=2, row=row)
def update():
myChar.add_stat(idx)
amount["text"] = myChar.stats[idx]
button = tk.Button(self.statsFrame, text="+", command=update)
button.grid(column=3, row=row)
myChar = Character(3)
root = tk.Tk()
myapp = Game(root)
root.mainloop()
This code is still not ideal, but it's an improvement. ;) For example, it would be good to give Game a method for creating new characters, rather than creating them in the global context. You could store them in a dict attribute of Game, using the character's name as the key.
Here's a new version that works on separate named stat attributes. As I said in the comments, doing it this way is more complicated (and less efficient) than using a list to hold the stats.
import tkinter as tk
class Character:
def __init__(self, availablePoints):
self.name = ""
self.experience = 0
self.level = 0
self.gold = 0
self.stat1 = 0
self.stat2 = 0
self.stat3 = 0
self.stat4 = 0
self.stat5 = 0
self.availablePoints = availablePoints
class Game:
def __init__(self, parent):
self.myParent = parent
self.myGame = tk.Frame(parent)
self.myGame.grid()
self.statsFrame = tk.Frame(self.myGame)
self.statsFrame.grid()
self.make_stat("Strength:", "stat1", 1, 1)
def make_stat(self, text, stat, column, row):
label = tk.Label(self.statsFrame, text=text)
label.grid(column=column, row=row)
amount = tk.Label(self.statsFrame, text=getattr(myChar, stat))
amount.grid(column=(column+1), row=row)
def update():
if myChar.availablePoints >= 1:
v = getattr(myChar, stat) + 1
setattr(myChar, stat, v)
myChar.availablePoints -= 1
amount["text"] = v
button = tk.Button(self.statsFrame, text="+", command=update)
button.grid(column=(column+2), row=row)
myChar = Character(5)
root = tk.Tk()
myapp = Game(root)
root.mainloop()
Related
I’m writing a code for an ecosystem simulation in which the user can adjust initial values as e.g. population size, fertility, and so on. Instead of writing a method for each variable I would like to use a general method that can be called for each variable. I also want to exclude non-integers as input and to restrict the acceptable value to a certain range.
However, the last requirement fails in my code as the range limits for each variable affect each other. Basically because I cannot add arguments to the entry.bind method. How to solve this?
Below, you'll find a simplified version of my code in which the problem happens.
import tkinter as tk
class Window():
def __init__(self):
tk.Label(master, text ='Fox number').grid(row=0,column=0)
tk.Label(master, text ='Hare number').grid(row=1,column=0)
self.fox_entry=tk.Entry(master, width=5)
self.fox_entry.grid(row=0, column=1)
self.hare_entry=tk.Entry(master, width=5)
self.hare_entry.grid(row=1, column=1)
class Ecosystem():
def __init__(self):
self.foxnumber = 10
self.harenumber = 100
def initiate(self):
def input_user(entry,value, minval, maxval):
self.value = value
self.inputvalue = value
self.minval = minval
self.maxval = maxval
def get_value(event):
try:
self.inputvalue = int(entry.get())
print(self.inputvalue)
except ValueError:
entry.delete(0,'end')
entry.insert(0,self.value)
self.inputvalue = self.value
if self.inputvalue not in range(self.minval, self.maxval+1):
entry.delete(0,'end')
entry.insert(0,self.value)
self.inputvalue = self.value
print(self.inputvalue)
entry.bind('<Return>', get_value)
value = self.inputvalue
return value
my_win.fox_entry.insert(0,self.foxnumber)
self.foxnumber = input_user(my_win.fox_entry,self.foxnumber, 0, 50)
my_win.hare_entry.insert(0,self.harenumber)
self.harenumber = input_user(my_win.hare_entry,self.harenumber, 0, 200)
# more variables will added later on
master = tk.Tk()
my_win = Window()
my_ecosystem = Ecosystem()
my_ecosystem.initiate()
master.mainloop()
im using tkinter with class abd im having trouble with adding a product
class Add_Page():
def __init__(self, child):
self.child = child
child.title = "Ajouter"
self.l1=Label(child,text="Ajouter produit :",bg="blue").grid(row=0,columnspan=2)
self.l2=Label(child,text="Matricule").grid(row=1,column=0)
self.vlrm = StringVar()
self.en2 = Entry(child, textvariable=self.vlrm, width=30).grid(row=1,column=1)
self.l3=Label(child,text="Nom").grid(row=2,column=0)
self.vlrn = StringVar()
self.en3 = Entry(child, textvariable=self.vlrn, width=30).grid(row=2,column=1)
self.l4=Label(child,text="Prix").grid(row=3,column=0)
self.vlrp = IntVar()
self.en4 = Entry(child, textvariable=self.vlrp, width=30).grid(row=3,column=1)
self.b2=Button(child,text="Valider",command=self.add_p).grid(row=4,columnspan=2)
#Add product function
def add_p(self):
print(self.vlrm.get())
print(self.vlrp.get())
the results are anempty chaine and 0
i dont seem to find the problem especially that i used the get method in users class and its working just fine
Heelp
You don't need create a variable to entry, only make this for radiobutton or checkbutton. And you can change your create of tkinter object, like that
change this
self.l1=Label(child,text="Ajouter produit :",bg="blue").grid(row=0,columnspan=2)
for this
self.l1 = Label(child, text = "Ajouter produit :", bg = "blue")
self.l1.grid(row = 0, columnspan = 2) # remove all variables StringVar() and IntVar()
if you need make some future change using .config or .get() you don't can make that in the first example. You can continue using variable, but i don't recommend that, if you make this change .get() will work now.
I maked a easy way to draw in tkinter, you can use or make change, is for python 2
from Tkinter import *
class Draw_tk():
Row, Column, List = 0, 0, []
def __init__(self, child):
self.child = child
child.title = "Ajouter"
def labelAndEntry(self, text): # def to create a entry and a label
self.l = Label(self.child, text = text) # create label
self.l.grid(row = Draw_tk.Row, column = Draw_tk.Column) # place label
Draw_tk.Column += 1 # add 1 in Column to place the entry
self.e = Entry(self.child, width = 30) # create entry
self.e.grid(row = Draw_tk.Row, column = Draw_tk.Column) # place entry
Draw_tk.List.append(self.e) # add the entry in a list
Draw_tk.Row, Draw_tk.Column = Draw_tk.Row + 1, 0
def label(self, text):
self.l = Label(self.child, text = text, bg = "blue") # def to create a simple label
self.l.grid(row = Draw_tk.Row, columnspan=2) # place the label
Draw_tk.Row += 1
def button(self, text, var): # create a simple button
self.b = Button(self.child, text = text, command = var) # create button
self.b.grid(row = Draw_tk.Row, column = Draw_tk.Column) # place the button
def valid():
for item in Draw_tk.List: # run a variable in your values list
print item.get() # get the value and print
root = Tk()
controller = Draw_tk(root) # create object Draw_tk
controller.label('Ajouter produit')
controller.labelAndEntry('Matricule')
controller.labelAndEntry('Nom')
controller.labelAndEntry('Prix')
controller.button('Valider', valid)
root.mainloop()
I am writing a program which analyses a CSV file containing my credit card monthly statement. I am trying to create a gui with tkinter in which I have a row of label headings followed by rows which consist of 3 more labels and then a combobox in the 4th column which has a number of categories. I have written a class which defines each row of the table. I want to pass a value to the instance of the class which specifies the initial value for the combobox. This is different for each instance or row. Or at least should be....
My problem is that in passing in different numbers for each row/instance I always get all rows having the value specified for the first created instance.
My classes are as follows:
class CatFormApp:
def __init__(self,parent):
self.parent = parent
self.datelabel = Tkinter.Label(root, text='Date',font ='Verdana 12 bold',borderwidth=5).grid(row=0,column=0)
self.transactionlabel = Tkinter.Label(root, text='Transactions',font ='Verdana 12 bold',borderwidth=5).grid(row=0,column=1)
self.paidoutlabel = Tkinter.Label(root, text='Paid Out',font ='Verdana 12 bold',borderwidth=5).grid(row=0,column=2)
self.catlabel = Tkinter.Label(root, text='Category',font ='Verdana 12 bold',borderwidth=5).grid(row=0,column=3)
class RowFormat:
def __init__(self,parent,rownumbval,defaultval,dateval,transactionval,paidoutval,CatTupleVals,bkg):
self.parent = parent
self.datevallabel = Tkinter.Label(root, text=dateval, borderwidth=5).grid(row=rownumbval,column=0)
self.transactionvallabel = Tkinter.Label(root, text=transactionval, borderwidth=5, background = bkg).grid(row=rownumbval,column=1)
self.paidoutvallabel = Tkinter.Label(root, text=paidoutval, borderwidth=5).grid(row=rownumbval,column=2)
self.combo(rownumbval,defaultval,CatTupleVals)
def combo(self,rownumb,default,CatTuple):
self.box_value = '1'
self.box = ttk.Combobox(self.parent,textvariable = self.box_value)
self.box['values']=CatTuple
self.box.current(default)
self.box.grid(row=rownumb,column=3)
The relevant bit of my main code is:
root = Tkinter.Tk()
app = CatFormApp(root)
row = ['']*(len(CreditData['Transactions'])+1)
for r in range(1,len(CreditData['Transactions'])):
if CreditData['Paid out'][r-1] != '':
if noMatch[r-1] == True:
print 1
row[r] = RowFormat(root,r,1,CreditData['Date'][r-1],CreditData['Transactions'][r-1],CreditData['Paid out'][r-1][1:],tuple(CatHeadings[:-1]),bkg = 'red')
else:
print 2
row[r] = RowFormat(root,r,2,CreditData['Date'][r-1],CreditData['Transactions'][r-1],CreditData['Paid out'][r-1][1:],tuple(CatHeadings[:-1]),bkg = 'white smoke')
root.mainloop()
In the above example all comboboxes have the value associated with 1. Whereas I should get some with a 1 and some with a 2 depending on the if statement. I'm puzzled because I do something very similar for the other label values passed and these all come out differently as expected.
If someone could explain where I'm going wrong I'd be really grateful
This code is wrong:
self.box_value = '1'
self.box = ttk.Combobox(...,textvariable = self.box_value)
The value for the textvariable attribute must be an instance of the tkinter class StringVar (or one of the other special tkinter variables).
Use it like this:
self.box_value = Tkinter.StringVar()
self.box = ttk.Combobox(self.parent,textvariable = self.box_value)
To set values, use:
self.box['values'] = ('A', 'B', 'C')
To select the first item, use:
self.box.current(0)
I am trying to make a button that when clicked updates the number on a label. What I am trying to accomplish is that when someone scores a goal, you can click the Goal! button and it will update the teams score.
import sys
from tkinter import *
root = Tk()
class team1:
score = 0
def goal(self):
self.score += 1
team1_attempt.set(text = self.score)
team1 = team1()
team1_attempt = Label(text = team1.score).pack()
team1_button = Button(text="Goal!", command = team1.goal).pack()
Hope someone can help! New to python.
You have two problems with your code.
First problem:
team1_attempt = Label(text = team1.score).pack()
This sets team1_attempt to None, because pack(0 returns None. If you want to save a reference to a widget so you can interact with it later you must do widget creation and widget layout in two steps.
Second problem:
team1_attempt.set(text = self.score)
To change an attribute of a widget, use the configure method. I don't know what documentation you read that says to call set on a label widget, but that documentation is wrong. Use configure, like so:
test1_attempt.configure(text=self.score)
Instead of using a label, try using an Entry widget that inserts the score into the Entry widget. For example:
class test:
def __init__(self, master):
self.goalButton = Button(master,
text = "goal!",
command = self.goalUpdate)
self.goalButton.pack()
self.goalDisplay = Entry(master,
width = 2)
self.goalDisplay.pack()
self.score = 0
def goalUpdate(self):
self.goalDisplay.delete(1.0, END) # Deletes whatever is in the score display
score = str(self.score)
self.goalDisplay.insert(0, score) # Inserts the value of the score variable
I've been building an app to track stock prices. The user should see a window with an entry widget and a button that creates a new frame with a label and a button. The label is the stock price and symbol, the button is a delete button, and should hide that frame if clicked.
I've re-written this program 4 times now, and it's been a great learning experience, but what I've learned is that I can't have the "mini-frames" being called from methods part of the main GUI class - this funks up the delete buttons, and updates the value behind frame.pack_forget() so it only deletes the last item ever.
I've moved my mini-frame widgets down into the class for the actual stock values. I've packed them (what I assume to be correct) but they don't show up. They also don't error out, which isn't very helpful. Here's my code, although I've omitted a lot of the functional parts to show what is happening with my frames. Keep in mind I need to keep it so that I can call my updater (self.update_stock_value) with a .after method against myapp.myContainer.
Is there a better way to do this?? Thanks in advance, my head hurts.
import re
import time
import urllib
from Tkinter import *
import threading
from thread import *
runningThreads = 0
# each object will be added to the gui parent frame
class MyApp(object):
def __init__(self, parent):
self.myParent = parent
self.myContainer = Canvas(parent)
self.myContainer.pack()
self.create_widgets()
# METHOD initiates basic GUI widgets
def create_widgets(self):
root.title("Stocker")
self.widgetFrame = Frame(self.myContainer)
self.widgetFrame.pack()
self.input = Entry(self.widgetFrame)
self.input.focus_set()
self.input.pack()
self.submitButton = Button(self.widgetFrame, command = self.onButtonClick)
self.submitButton.configure(text = "Add new stock")
self.submitButton.pack(fill = "x")
# METHOD called by each stock object
# returns the "symbol" in the entry widget
# clears the entry widget
def get_input_value(self):
var = self.input.get()
self.input.delete(0, END)
return var
# METHOD called when button is clicked
# starts new thread with instance of "Stock" class
def onButtonClick(self):
global runningThreads # shhhhhh im sorry just let it happen
runningThreads += 1 # count the threads open
threading.Thread(target = self.init_stock,).start() # force a tuple
if runningThreads == 1:
print runningThreads, "thread alive"
else:
print runningThreads, "threads alive"
def init_stock(self):
new = Stock()
class Stock(object):
def __init__(self):
# variable for the stock symbol
symb = self.stock_symbol()
# lets make a GUI
self.frame = Frame(myapp.myContainer)
self.frame.pack
# give the frame a label to update
self.testLabel = Label(self.frame)
self.testLabel.configure(text = self.update_stock_label(symb))
self.testLabel.pack(side = LEFT)
# create delete button to kill entire thread
self.killButton = Button(self.frame, command = self.kill_thread)
self.killButton.configure(text = "Delete")
self.killButton.pack(side = RIGHT)
# create stock label
# call updater
def kill_thread(self):
global runningThreads
runningThreads -= 1
self.stockFrame.pack_forget() # hide the frame
self.thread.exit() # kill the thread
def update_stock_label(self, symb):
self.testLabel.configure(text = str(symb) + str(get_quote(symb)))
myapp.myContainer.after(10000, self.update_stock_label(symb))
def stock_symbol(self):
symb = myapp.get_input_value()
print symb
# The most important part!
def get_quote(symbol):
try:
# go to google
base_url = "http://finance.google.com/finance?q="
# read the source code
content = urllib.urlopen(base_url + str(symbol)).read()
# set regex target
target = re.search('id="ref_\d*_l".*?>(.*?)<', content)
# if found, return.
if target:
print "found target"
quote = target.group(1)
print quote
else:
quote = "Not Found: "
return quote
# handling if no network connection
except IOError:
print "no network detected"
root = Tk()
root.geometry("280x200")
myapp = MyApp(root)
root.mainloop()
Your code won't run because of numerous errors, but this line is definitely not doing what you think it is doing:
self.frame.pack
For you to call the pack function you must include (), eg:
self.frame.pack()
You ask if your code is the best way to do this. I think you're on the right track, but I would change a few things. Here's how I would structure the code. This just creates the "miniframes", it doesn't do anything else:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.submit = tk.Button(self, text="Submit", command=self.on_submit)
self.entry.pack(side="top", fill="x")
self.submit.pack(side="top")
def on_submit(self):
symbol = self.entry.get()
stock = Stock(self, symbol)
stock.pack(side="top", fill="x")
class Stock(tk.Frame):
def __init__(self, parent, symbol):
tk.Frame.__init__(self, parent)
self.symbol = tk.Label(self, text=symbol + ":")
self.value = tk.Label(self, text="123.45")
self.symbol.pack(side="left", fill="both")
self.value.pack(side="left", fill="both")
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()