Out a value between the brackets in a function - python-3.x

from tkinter import *
import pygame.mixer
app = Tk()
app.title("Head First Mix")
sound_file = "50459_M_RED_Nephlimizer.wav"
mixer = pygame.mixer
mixer.init()
def track_start():
track.play(loops = -1)
def track_stop():
track.stop()
def shutdown():
track.stop()
app.destroy()
def track_toogle():
if track_playing.get() == 1:
track_start()
else:
track_stop()
def change_volume(v):
track.set_volume(volume.get())
track_playing = IntVar()
track = mixer.Sound(sound_file)
track_button = Checkbutton(app,
variable = track_playing,
command = track_toogle,
text = "50459_M_RED_Nephlimizer.wav")
track_button.pack(side = LEFT)
volume = DoubleVar()
volume.set(track.get_volume())
volume_scale = Scale(app,
variable = volume,
from_ = 0.0,
to = 1.0,
resolution = 0.1,
command = change_volume,
label = "Volume",
orient = HORIZONTAL)
volume_scale.pack(side = RIGHT)
app.protocol("WM_DELETE_WINDOW", shutdown)
app.mainloop()
for the moment i read a book called head first python. i understoop everything until i reached chapter 9. why dont my app work if i dont place something in the change_volume functions brackets
why do i need the"v" -
def change_volume(v):
The let you stop, start and adjust volume for a track btw

The code
def change_volume(v):
track.set_volume(volume.get())
is written to require a parameter v. However, as written, it never actually uses the parameter. Instead, since you called volume = track.get_volume() elsewhere in the code, unless you modify volume (or the value of the track's volume) somewhere else, your code is effectively equivalent to
def change_volume(v):
track.set_volume(track.get_volume())
If you want to actually change the value, you need to use the value of the parameter to set the volume:
def change_volume(v):
track.set_volume(v.get())

Related

How to append the same variable multiple times that defines a class

Here is the class NPC:
class NPC:
def __init__(self):
self.x = randint(0,800)
self.y = randint(0,60)
self.velocity_x = 5
self.drop_y = 60
self.img = "my image path here"
npc = NPC()
num_npc = 5
list = []
for i in range(num_npc):
list.append(npc)
In the game loop only one image is shown and is stationary.
I'm working on trying to write old code to be object oriented and can't figure out the best way to render the npcs
Below is the old code I was using and it worked as expected
npc_img = []
npc_x = []
npc_y = []
npc_vel_x = []
npc_vel_y = []
num_of_npc = 5
for i in range(num_of_npc):
npc_img.append("my img path")
npc_x.append(random.randint(0, 800))
npc_y.append(random.randint(0, 60))
npc_vel_x.append(4)
npc_vel_y.append(40)
Your code is pretty much correct already. However the way you are creating instances of NPC objects is not quite correct. I guess you meant to add 5 NPCs to the list, not 5 references to the same NPC object. That is what your question title says though!
npc = NPC()
...
for i in range(num_npc):
list.append(npc) # <<-- HERE, same object, 5 times
The code should call the NPC constructor in the loop, rather than outside it.
for i in range( num_npc ):
new_npc = NPC()
list.append( new_npc )
While you're rewriting code, it might be worth keeping the co-ordinates and image dimensions in a Pygame Rect, since this allows for easy collision detection and other nice things.
Something like:
class NPC:
def __init__(self):
self.image = pygame.image.load( "image path here" ).convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = randint(0,800)
self.rect.y = randint(0,60)
self.velocity_x = 5
self.drop_y = 60
def draw( self, screen ):
screen.blit( self.image, self.rect )
def hitBy( self, arrow_rect ):
hit = self.rect.colliderect( arrow_rect )
return hit
If I understood correctly, something like this should work:
class NPC:
def __init__(self):
self.x = randint(0,800)
self.y = randint(0,60)
self.velocity_x = 5
self.drop_y = 60
self.image_list = []
self_image_load_dict = {}
def add_image(self, image_path):
self.image_list.append(image_path)
def load_images(self):
self.image_load_dict[]
for i in len(self.get_image_list()):
self.image_load_dict[i] = pygame.image.load(self.get_image_list()[i])
def get_image_list(self):
return self.image_list
def get_image_load_dict(self):
return self.image_load_dict
I used fstring so it would be easier to load images and keep track of the image number:
npc = NPC()
for i in range(NUMBER_OF_NPC):
npc.add_image(f"image_path_{i}")
Now you have image_paths in the object's list, I assume you want to load them, hence the load_images method.
NOTE: If needed you can create additional method(s) for loading images. E.g. if you have animations for "left" and "right" movement
I hope this answers your question, if I omitted something please say in comment.

How do I get the value from an option in tkinter after it has passed through a function?

I'm using tkinter to create an option menu for a user to interact with, when the selection has been made it will pass through a function to return an integer based on the input. I want to be able to return the integer back to a variable outside of the function so it can be received by another file. My problem is that python will keep returning the button as the command has not yet been processed.
from tkinter import *
Final = [] ##List containing options
master = Tk()
variable = StringVar(master)
variable.set(Final[0]) # default value
w = OptionMenu(master, variable, *Final)
w.pack()
def ok():
global choice
choice = variable.get()
global x
x = 0
for i in Final:
if str(i) == str(choice):
break
x += 1
button = Button(master, text="Choose", command=ok)
button.pack()
values = x

Unable to bind function after pickling - tkinter

I have simple code which creates two fields by the press of a button. There are two other buttons to save and load back the entry fields created. I have used the bind function to bind field A and field B. Pressing the Enter button on field A after entering a number will print out its value multiplied by 5 in field B. At this point the bind function works perfectly.
When I create three entry fields and save the progress without entering any inputs and compile the program, then load the file, the bind function does not seem to work. It seems to work only for the last field created. My code is as follows. I tried my best to simplify the code.
from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import asksaveasfile
from tkinter import messagebox
import pickle
class Test(Frame):
def Widgets(self):
self.button_add = Button(self, text = "Add", command = self.add)
self.button_add.grid(row=0, column =2)
self.button_save = Button(self, text = "save", command = self.save)
self.button_save.grid(row=0, column =3)
self.button_load = Button(self, text = "load", command = self.load)
self.button_load.grid(row=0, column =4)
def add(self):
def test(event):
self.field_B[n].delete(0, END)
self.field_B[n].insert(0, (float(self.field_A[n].get()))*5)
self.field_A.append({})
n = len(self.field_A)-1
self.field_A[n] = Entry(self)
self.field_A[n].grid(row=n, column =0)
self.field_A[n].bind("<Return>", test)
self.field_B.append({})
n = len(self.field_B)-1
self.field_B[n] = Entry(self)
self.field_B[n].grid(row=n, column =1)
def save(self):
for n in range(len(self.field_A)):
self.entry_A.append(self.field_A[n].get())
self.entry_B.append(self.field_B[n].get())
fname = asksaveasfile(mode = "w", defaultextension = ".est")
data = {"fields": len(self.field_A), "entries_A": (self.entry_A),"entries_B": (self.entry_B)}
with open(fname.name, "wb") as file:
pickle.dump(data, file)
def load(self):
def test(event):
print("Why is the value of n always equal to", n, "?")
self.field_B[n].delete(0, END)
self.field_B[n].insert(0, (float(self.field_A[n].get()))*5)
fname = askopenfilename(filetypes = (("Estimation Files (est)", "*.est"),))
location = fname.replace("/", "\\")
if location:
with open(location, "rb") as file:
data = pickle.load(file)
for n in range(data["fields"]):
self.field_A.append({})
self.field_A[n] = Entry(self)
self.field_A[n].grid(row=n, column =0)
self.field_A[n].insert(0, data["entries_A"][n])
self.field_A[n].bind("<Return>", test)
self.field_B.append({})
self.field_B[n] = Entry(self)
self.field_B[n].grid(row=n, column =1)
self.field_B[n].insert(0, data["entries_B"][n])
def __init__(self,master = None):
Frame.__init__(self, master)
self.field_A = []
self.field_B = []
self.entry_A = []
self.entry_B = []
self.grid()
self.Widgets()
root = Tk()
app = Test(master = None)
app.mainloop()
You need a "closure". You can make a closure in python with the functools.partial function.
from functools import partial
def test(n, event=None):
self.field_B[n].delete(0, END)
self.field_B[n].insert(0, (float(self.field_A[n].get()))*5)
#other code ...
self.field_A[n].bind("<Return>", partial(test, n))
Both of your test() functions are accessing a variable n from the enclosing function. In the case of add(), there is no loop; n has a single value. Each Entry's test() gets its own n, because they were bound by a distinct call to add(). In load(), however, you are looping over n values; each test() is referring to the same n, which will have its final value by the time that any binding can possibly be invoked. The other answer gives a reasonable way to give each instance of test() its own personal n, so I'm not going to repeat that here.

How to loop this code?

I have trying to loop this but fail everytime.
It´s the def create_widgets I try to loop. So I got a GUI that show a red button/box as soon something goes offline.
This is the code I tried to use.
from tkinter import *
class Application(Frame):
""" GUI """
def __init__(self, master):
""" Initialize the Frame"""
Frame.__init__(self,master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""Create button. """
import os
#Router
self.button1 = Button(self)
self.button1["text"] = "Router"
self.button1["fg"] = "white"
self.button1.grid(padx=0,pady=5)
#Ping
hostname = "10.18.18.1"
response = os.system("ping -n 1 " + hostname)
#response
if response == 0:
self.button1["bg"] = "green"
else:
self.button1["bg"] = "red"
root = Tk()
root.title("monitor")
root.geometry("500x500")
app = Application(root)
root.mainloop()
You can attach it onto Tk's event loop using Tk's after method.
def if_offline(self):
#Ping
hostname = "10.18.18.1"
response = os.system("ping -n 1 " + hostname)
#response
if response == 0:
self.button1["bg"] = "green"
else:
self.button1["bg"] = "red"
Then, this line goes anywhere between app = Application(root) and root.mainloop():
root.after(0, app.if_offline)
after attaches a process onto the Tk event loop. The first argument is how often the process should be repeated in milliseconds, and the second is the function object to be executed. Since the time we have specified is 0, it will constantly check and constantly update the button's background color. If that churns your CPU, or you don't want to be pinging that much, you can change the repeat time to something larger.
The function object passed in should be just that: a function object. It has the same rules as the command argument in a Button constructor.
If you need to pass in arguments to the function, use a lambda like so:
root.after(0, lambda: function(argument))
This works because the lambda function returns a function object, which when executed will run function(argument).
Source

Making a basic user interface with tkinter-python

I've just been trying to practice some code by making a simple dice game
and using tkinter for the user interface of the starting menu for the game
For the starting menu, I'm trying to just see how it will come out if I used the code below,
BUT before making the Button widget and the Label Widgets, the commands come up first.
How would I fix this up?
thanks in advance
import tkinter as tk
from main import main
from written import showInstructions, showCredits
from generate_no import generate_no
class DiceGameUI(tk.Frame):
def __init__(self, master = None):
tk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
self.titleLabel = tk.Label(self, fg = "red") #The Title of the Game
self.titleLabel["text"] = "Dice Game"
self.startButton = tk.Button(self) #Start Button
self.startButton["text"] = "Roll On!"
self.startButton["command"] = main() <<----- This plays out first before
self.startButton.grid() making any widgets
self.instrButton = tk.Button(self) #Instructions Button
self.instrButton["text"] = "Instructions"
self.instrButton["command"] = showInstructions()
self.instrButton.grid()
self.credits = tk.Button(self) #Credits Button
self.credits["text"] = "Credits"
self.credits["command"] = showCredits()
self.credits.grid()
root = tk.Tk() #Run code using tkinter
app = DiceGameUI(master = root)
app.mainloop()
'
You have to assign only name of function without () and arguments
self.startButton["command"] = main
If you use () than you run that function and result is assigned to command. It is good to create dynamicly function for command.
If you will need assign function which require arguments you have to use lambda function.
self.startButton["command"] = lambda:main()
self.startButton["command"] = lambda:main("abc", 123)
a = "abc"
b = 123
self.startButton["command"] = lambda arg1=a,arg2=b:main(arg1,arg2)
self.startButton["command"] = lambda title=a,count=b:main(title,count)
# this may not work - especially if a or b changes value (for example in loop)
self.startButton["command"] = lambda:main(a, b)
example how to use function name in own code
def plus(a, b):
return a + b
def minus(a, b):
return a - b
def result(a, b, func_name):
return func_name(a,b)
print result(10, 7, plus) # 17
print result(10, 7, minus) # 3

Resources