Running a main function for several objects - python

I have a kind of tricky problem here. Basically what I want to do is have a tamaguchi-like program, where people get to choose what the tamaguchi does and it either increases in size or decreases. This works great for there's only one tamaguchi! But it should be possible to create and manage several tamaguchis simultaneously. I run into big problems when I try this though. Basically this is how I've been thinking so far:
class Tamaguchi(object):
def __init__(self,name,size=1):
self.name=name
self.size=size
def increasesize(self):
self.size+=1
GoodBadChange.config(text="Good job. You increased in size!")
def decreasesize(self):
self.size-=1
GoodBadChange.config(text="Bad job. You decreased in size!")
def main():
print(name)
root = tkinter.Tk()
global root
root.title("Tamaguchi-spelet")
root.geometry("900x900")
getLists()
getPhotos()
Picture = tkinter.Label(root, image=normal)
Picture.pack()
global Picture
ScoreBoard = tkinter.Label(root, text="Score " + str(tamaguchin.size), font=('Helvetica', 15))
ScoreBoard.pack()
global ScoreBoard
GoodBadChange = tkinter.Label(root, text="", font=('Helvetica', 12))
GoodBadChange.pack()
global GoodBadChange
LatestAction = tkinter.Label(root, text="", font=('Helvetica', 12))
LatestAction.pack()
global LatestAction
app = Application(root)
app.pack()
updateDisplay()
LatestAction.config(text="Hello and welcome to the tamaguchi game!\nThe tamaguchi starts the day with the size "+str(tamaguchin.size)+"!\nThe tamaguchi starts the day off with the last three actions of "+lista[0]+"-"+lista[1]+"-"+lista[2]+"\n")
root.mainloop()
tamaguchi_list=[]
amount=int(input("Number of tamaguchis in your farm: "))
for i in range(amount):
name = input("What do you want to name your tamaguchis?: ")
tamaguchi_name = Tamaguchi(name)
tamaguchi_list.append(tamaguchi_name)
for tamaguchis in tamaguchi_list:
main()
for tamaguchis in tamaguchi_list:
print(name,"reached a size of ",name.size,"!")
Sorry it's a bit long, I've still shortened off the parts that aren't relevant. But I was thinking, we create a list with all the tamaguchis in them, and then we just run the main function for each tamaguchi in the tamaguchi-list. That way, for example "erik" gets one score, and "mary" another, and this should then be written in the end. However, this does not seem to work, as you can see I write "print(name)" in the beginning of the main function just to see that it actually goes through all the names in the list, but in fact it just prints the same name over and over again. I have no idea why it doesn't go through all names.
Another problem is the fact I have written in the main function stuff like str(tamaguchin.size) when I want to show the size of the tamaguchi, but this was because when I only had one tamaguchi I just created it in the beginning and I could just refer to that in the rest of the program (tamaguchin=Tamaguchi('SomeName') is what I used to have!) Can this be solved?
Thanks a lot for any help, I'm really stuck with this.
edit: Perhaps it's unclear since I don't show all of the code. I thought it might just be too long, but perhaps it's better to understand what I mean! I uploaded it here!

This chapter describes a game in which the player causes a creature to "increase in size" due to an event in the game: http://inventwithpython.com/pygame/chapter6.html
Read through the game design and similar to increasing the size you can decrease the size of the creature.
In order to manage multiple creatures read this chapter: http://inventwithpython.com/pygame/chapter8.html

I need you to be more specific in which lines of your code your error is occurring to properly answer this question. That being said, however, I would recommend using a more efficient structure to manage your tamaguchins.
What I understood you describing was 'tamaguchi does something' then 'tamaguchi grows or shrinks'.
Try something like this using a dictionary:
tamaguchins = {"ralph":10, "george":5, "chester":2}
def size_modify(tamaguchin, plus_minus, tamaguchins):
tamaguchins[tamaguchin] += plus_minus
return tamaguchins
Call it like this:
tamaguchins = size_modify("ralph", -1, tamaguchins)
Or like this:
tamaguchins = size_modify("george", 1, tamaguchins)
Or however you like, just remember that in this case you must always precede the calling of the function with 'tamaguchins =' and your third argument must always be tamaguchins.
Cheers

Related

Error: "NameError: name 'self' is not defined" when using exec() to create several buttons in tkinter

I am trying to create a GUI for a cribbage program that I made a while back. I want to fan the cards out at the beginning so the player can select where they would like to cut the deck. To avoid individually declaring and packing 52 different frames and buttons manually, I used a for loop and the exec() method to create each button and frame and also pass an integer into the method in the command= parameter of the buttons. It works as intended to create all of the buttons, but when I click on one of the cards, the NameError is raised.
def cut(self, i):
self.select_card_frame.place_forget()
for k in range(52):
exec(f'self.cut_frame_{k}.place_forget()')
return i
def start_cut(self):
global cut_image
cut_image = self.resize_card('cards/back_of_card.png')
self.select_card_frame = Frame(self.window)
self.select_card_label = Label(self.select_card_frame, text='Select Card to Cut', font=20)
self.select_card_label.pack()
self.select_card_frame.place(x=450, y=50)
for i in range(52):
exec(f'self.cut_frame_{i} = Frame(self.window)')
exec(f'self.cut_label_{i} = Button(self.cut_frame_{i}, image=cut_image, command=lambda: self.cut({i}))')
exec(f"self.cut_label_{i}.pack(side='left')")
exec(f'self.cut_frame_{i}.place(x={i*20}, y=100)')
I have tried making a few of the card buttons individually and it worked as it should, so I suspect it is something that I am doing wrong with the exec() method. Any ideas on what I am doing wrong? or is there a better way to do this without using exec()? or do I just have to suck it up and type out the extra 300 lines of code? This is what the window looks like for a better picture of what I am trying to do.
Deck fanned out for cut

(Python 3.7) How can i print messages characters with a delay between them using tkinter?

First of all i'm new to python and coding
I want to do something pretty simple with tkinter, when you hit a button it show you a text, like in old games, letter by letter with a little delay between each characters
I can't find a way to make the delay between characters, i've tried time.sleep with a loop but the text is shown at the end of the loop
I've seen the after function but i don't know how to use it neither i understand how it works
Should i use sleep or after ? And how should i use them to make it work ?
Btw if you have any tips or advices about the code tell me
#MainFrame
root.title("Project")
root.geometry('400x400')
root.configure(bg="plum1")
#Frame
BlackBorder=Frame(root,width=400,height=300,bg='Black')
BlackBorder.place(x=0,y=80)
TxtFrame=Frame(BlackBorder,width=370,height=270,bg='lavender')
TxtFrame.place(x=15,y=15)
#Display
Cunter=Text(root,width=24,height=1,bg='lavender',font='Fixedsys')
Cunter.place(x=100,y=22)
Cunter.insert(END, str(len(LoList))+" Résultats Différents")
#defTxt
def LoMsg(self):
self=Text(TxtFrame,wrap='word',borderwidth=0,width=35,height=10,bg='lavender',font='Fixedsys')
self.place(x=50,y=100)
LoTxt=str(LovList[randrange(len(LovList))])
LoNum=0
while LoNum!=len(LoTxt):
self.insert(END,LoTxt[LoNum])
sleep(0.1)
LoNum+=1
#Button
buttonMain=Button(root,width=9,height=3,bg='thistle2',text="Try me",font='Fixedsys')
buttonMain.place(x=5,y=5)
#ButtonEvent
buttonMain.bind('<1>', LoMsg)
The following is an example to highlight the usage of the after(ms, callback) method to get your desired result (adjust the ms in the after method accordingly):
import tkinter as Tk
def insert():
global LoNum
text.insert(Tk.END, word[LoNum])
LoNum += 1
if LoNum != len(word):
root.after(300, insert)
else:
return
root = Tk.Tk()
root.geometry('600x200')
LoNum = 0
word = [x for x in 'abcdefg'] # get the word to a list format
text = Tk.Text(root, wrap='word', borderwidth=0, width=35, height=10, bg='lavender')
text.pack()
Tk.Button(root, text='Try Me', width=9, height=3, bg='thistle2', command=insert).pack()
root.mainloop()
Welcome to Python and coding! There's a few things I want to address before I answer your question:
It is helpful if you can provide a minimal, reproducible example when asking your question. I can't really see what the issue in your code is because there's so much happening and I can't run it on my computer because there are parts missing. For example, LoList is undefined and there are no import statements.)
PEP8 has a huge list of suggestions on how to style your code - too much to read all at once. But I'd like to draw your attention to Function and Variable names, which suggests that variables and functions use all lowercase, with words separated by underscores. Usually Class Names start with a capital letter. So a lot of your variables and functions look like classes to me.
To answer your question, I think that using time.sleep() would work. Here's a simple example that runs in a console that might help you figure out how to use it:
import time
text = "I've been awaiting your arrival..."
for char in text:
# end='' will prevent each letter from getting its own line on the console
# flush=True will make sure that the character is printed right away in the console
print(char, end='', flush=True)
time.sleep(0.1)

how to send information to a define with a tkinter button

I'm trying to create a menu for a school project and wanted to make it so that it reads the files in a given directory and creates buttons on the screen based on what files are there. My main problem at the moment is that I can't get the Button to send the information i need.
I've tried to use the functions in a different way but this is the closest I can get to what I want.
class Menu(Frame):
def __init__(self, master=None):
#working out how big to make the window
global files_in_area
files_in_area = []
files_in_area = os.listdir(menu_folder_loc)
print files_in_area
a = len(files_in_area)
b = 3
rows = a / b
rows = rows + 1
window_height = rows * 56
root.geometry('%dx%d+%d+%d' % (450, window_height, 0, 0))
Frame.__init__(self, master)
self.master = master
self.init_menu()
def init_menu(self):
self.master.title("Menu")
self.pack(fill=BOTH, expand=1)
for i in range(0, len(files_in_area)):
#button placment
a = i
b = 3
row_no = a / b
row_no = row_no + 1
column_no = a % b
global file_name
file_name = str(files_in_area[i])
b1 = Button(self, text=file_name, bg= "red", height=3, width=20, command=self.client_print_file).grid(row=row_no, column=column_no, sticky=W)
def client_print_file(self):
print file_name
I've got the code to work when finding the files and putting them in a tkinter window, and when I click on the button, it should open the file (I'm just printing the file name at the moment to make sure it works). However it just prints the last file in the list.
Sorry if this question is a bit confusing, and thanks in advance.
This is not a problem about tkinter but about scopes and oop in python. I assume you got started with python by reading online stuff or got a fast introduction. My first advice is: do not use/copy from someone else's code something if you do not understand it. If you copy, try to dig in yourself and MAKE it your own code. One good way of reading up stuff is the very good online documentation of python.
Some exercises to get you started: 1) Research about python's scoping of variables.
2) More importantly: read up on classes, i.e. object oriented programming.
To the code: 1) Ditch the use of global alltogether. No need here. If you want to use the variable files_in_area in all methods of the class simply make it an attribute of the class, just like you did with master which you then go on to use in another method where it was not defined! Now to the variable filename: Again, ditch global. Then filename lives in the scope of the for loop which is exactly what you want here.
See it like this: if you had not used the variable filename at all but simply put text=str(files_in_area[i]) inside the Button-constructor as argument, you wouldnt have run into this problem here - at its core this really is a problem about thinking for yourself and not copying too much code from someone else - I know it because this was a big issue for me for long time.
The hardest thing here is how to pass a command depending on the i of the for loop to each button. You can find a lot here on stackoverflow. This again is about variables and their scope but also about their binding. In each loop pass, we want to generate a value of command depending on i. One way to use this is using the (at first mystical) lambda: command=lambda i=i: self.client_print_file(i). And define def client_print_file(self, i): ... with an additional argument i. The critical thing here is the binding. What would not work is command=lambda i: self.client_print_file(i).
(Another way instead of using lambda would be using partial from functools...)
Assignment: read up on lambda and what it is about and about name binding and variables in python :)

Tkinter buttons and classes. How can i effectively call a function in a class through a button?

So I'm writing this program, to draw disks using turtle. I'm doing a tkinter interface, using buttons etc. but, I don't seem to be able to execute a function inside a class through the buttons. It prompts me with this classic python error, "turtleInput() missing 1 required positional argument: 'numPressed'"
I've tried it a million times, a million ways, I just can't see the problem, maybe one of you can. I'm gonna provide you with the function inside the class and the the button ( in code of course ) hopefully you can help me out. feel free to ask questions if you don't quite understand what I am saying.
def turtleInput(self, numPressed):
self.length = int(numPressed)
self.lstColor = ["maroon","brown","red","orange","yellow",
"green","lightgreen","purple","blue",
"lightblue"]
for i in range(0,self.length):
self.shrink = 220
self.shrinkLst = []
while self.shrink > 0:
self.shrink = self.shrink-20
self.shrinkLst.append(self.shrink)
self.diskCol = self.lstColor[i]
self.turtleDisks(self.diskCol,self.shrinkLst[i])
now the code for the button
num2= Button(root, text="2", width=3)
num2["command"]= lambda: Disk.turtleInput(2)
num2.grid(row=1, column=0, sticky=W, padx=3)
keep in mind that I imported tkinter, turtle and everything else works fine, that is the only problem.
You need to be referencing a Disk object, not the Disk class when referencing a function.
The solution looks like this:
# create instance of Disk
disk = Disk()
...
num2["command"]= lambda: disk.turtleInput(2)

Beginner python ( Tkinter ) - How to ask for a rate to make a currency conversion?

I am a beginner in programming so I'm sorry if the code is confusing or too long.
My question is: I am stuck with the rate input and how to put everything together to make the conversion works?
Thank you.
from tkinter import *
def convertDtoE():
fromDtoE=fromDtoEVar
fromDtoEVar.get()
fromEtoD=fromEtoDVar
fromEtoDVar.get()
fromDtoEVar.set(fromEtoD)
def convertEtoD():
fromDtoE=fromDtoEVar
fromDtoEVar.get()
fromEtoD=fromEtoDVar
fromEtoDVar.get()
fromEtoDVar.set(fromDtoE)
def main():
window=Tk()
global fromDtoEVar
fromDtoEVar=DoubleVar()
fromDtoEVar.set(0.0)
global fromEtoDVar
fromEtoDVar=DoubleVar()
fromEtoDVar.set(0.0)
aa=Label(window,text="Dollars")
aa.pack()
a=Entry(window,textvariable=fromDtoEVar)
a.pack()
rr=Label(window,text="Rate")
rr.pack()
rate=Entry(window)
rate.pack()
bb=Label(window,text="Euros")
bb.pack()
b=Entry(window,textvariable=fromEtoDVar)
b.pack()
c=Button(window, text="Convert Euros", command=convertEtoD)
c.pack()
d=Button(window, text="Convert Dollars", command=convertDtoE)
d.pack()
window.mainloop()
main()
I'm not sure exactly what you're asking, but there's one very big problem with your code that you repeat multiple times, so I'll assume that's what you want fixed:
fromDtoE=fromDtoEVar
fromDtoEVar.get()
This does nothing useful. It returns the current value of the DoubleVar, which you then ignore. Meanwhile, you've bound another name to the DoubleVar itself, but then you use the original name. I think what you wanted is:
fromDtoE=fromDtoEVar.get()
Now, at least, you'll be copying actually numbers around, instead of the string representations of Tk variables like PY_VAR0.
You're still not actually doing any conversions, just copying the numbers back and forth. To fix that, first you're going to need to actually access the value in the Rate widget. You can attack a DoubleVar to that the same way you do the other two widgets, access its value in the same way, and then just do the math.

Categories