Generate buttons in Tkinter with a different name each time - python

I want to generate a button for each .py document in a file, and each button give a different argument to the function
for name in files:
if name[-3:]=='.py':
a=name[:-3]
button=Button(barre, text=a, command=python_file(a))
button.pack()
def python_file(name):
text = Text(barre)
text.insert(name)
os.popen("python3 "+a+".py","r")
It basicly write the name of the python file and execute. But, I need to give a different name to each button, just like : button1, button2 (...).
Also when I run my code, It execute the function each time thee is a new python file.
Thx for helping !!

Change the command argument in your button to:
button=Button(barre, text=a, command=lambda a=a: python_file(a))
The argument passing to your function is the one which is the last value in the for loop. To pass the current value, you need to specify it through the lambda function.
Also, change the os.popen method like this to fully execute the code inside the file.
c = os.popen("python3 "+a+".py","r")
print(c.read())

Related

Label appearing conditionally

I am trying to make a label appear if the condition of my entry (textbox) is met. Unfortunately I cannot see anything when I am pressing the button on the testing. Here is what I have:
from tkinter import *
main= Tk()
firstname=Entry(main).place(x=30, y=50)
def register():
if any(i.isdigit() for i in firstname.get())== True:
print (Label(main,text='no numbers please').place(x=30, y=180))
else:
print(Label(main, text='pass').place(x=40, y=170))
register=Button(main,text='REGISTER', command= lambda :register).place(x=300, y=200)
There are at least three problems with your code. The first is in how you define the button's command:
register=Button(main,text='REGISTER', command= lambda :register)
When you do command=lambda: register, you're telling the button "when you're clicked run the code register". register all by itself does nothing. Since register is (supposed to be) a function, you need to call it like register() inside the lambda.
Since you aren't passing any values to the function, the lambda is completely unnecessary. Instead, just directly reference the function: command=register without the parenthesis.
The second problem is that you've used the name register to be two different things: a function and a reference to a widget. Because of the ordering of the code, command=register or command=lambda: register() will try to call the button rather than the function.
The third problem is a very, very common mistake. In python, when you do x = y().z(), x is given the value of z(). Thus, register = Button(...).pack(...) returns the value of pack(...) and pack (and grid and place) always returns None.
Therefore, you've set register to None, and when you try to call it you get NoneType object is not callable.
In addition to fixing the command, you need to pick a different name for either the function or the button. And you should not be calling place (or pack or grid) in-line with creating the widget. They should be separate steps.
So, putting that all together, you need to define firstname like this so that firstname is not None:
firstname=Entry(main)
firstname.place(x=30, y=50)
And then you need to define the button like this:
register_button = Button(main,text='REGISTER', command= register)
register_button.place(x=300, y=200)

How to change label (image form) with next button python

I am currently a novice in python and I'm trying to make a label switch from one image to another by clicking a next button. Here's my code:
from tkinter import *
def next1():
global slide
slide=1
if slide==1:
bglabel.config(image=bg1)
elif slide==2:
bglabel.config(image=bg2)
slide+=1
window.update()
window=Tk()
window.geometry("1500x750+0+0")
bg1=PhotoImage(file="backslide1.png")
bg2=PhotoImage(file="backslide2.png")
nextbutton=PhotoImage(file="next.png")
bglabel=Label(window, image=bg1)
bglabel.place(x=600,y=200)
nextbutton1=Button(window, image=nextbutton, bd=0, command=next1())
window.bind('<Button-1>', next1())
I sat for a good hour or so trying to tamper with the slide variable (trying to declare it before def, removing global, changing value, changing where slide+=1 is, etc) but one of two things always happens; either it's stuck on bg1 with the button clicking but doing nothing, or jumping straight to bg2. I've also tried splitting next1 into two different def's, one for variable tracking, one for switching bglabel, but still the same output. Please help.
(Also, will that window.bind be trouble as I continue to add buttons? If so please let me know how to do it correctly.)
As you mentioned, one 'error' that occurs is that the image immediately jumps to image bg2. This is the line causing that:
nextbutton1=Button(window, image=nextbutton, bd=0, command=next1())
More specifically, where you declare the command associated with the button:
command=next1()
With the enclosed brackets, you're calling the function next1 i.e. as soon as the button is created, run the specified function.
To solve this, just remove the pair of brackets:
nextbutton1=Button(window, image=nextbutton, bd=0, command=next1)
The same goes for your key binding. This way, the button/key now has a reference to the function - it knows what function to run and will run it when the specified action is performed.
More about the key binding...
When you use bind to assign a key to run a function, whatever function that is to be run needs to be made aware as such. Currently, the next function you are trying to bind is given no indication that it can be called using a keyboard button event. To fix that, we set a default parameter in next specifying the event:
def next1(event=None):
#rest of function code here
window.bind('<Button-1>', lambda event: next(event))
Setting a default parameter, event=None, basically means if no value forevent was passed to the function from whatever called it, set it to None by default (in that sense, you can choose to set it to whatever by default). Using lambda for the key bind in this way allows us to pass parameters to functions. We specify what parameter(s) we want to pass to the function and then specify the function, with the parameter(s) enclosed in brackets.
You need to provide the function, not the result of the function. So no parenthesis. Like this:
nextbutton1=Button(window, image=nextbutton, bd=0, command=next1)
Also remove the window.bind line, and your loop logic is broken. "slide" is always 1 since you set that in the function. Are you trying to cycle between the 2 images with every click? If so use itertools.cycle:
from tkinter import *
from itertools import cycle
def next1():
bglabel.config(image=next(bgimages))
window=Tk()
window.geometry("1500x750+0+0")
bg1=PhotoImage(file="backslide1.png")
bg2=PhotoImage(file="backslide2.png")
bgimages = cycle([bg1, bg2])
nextbutton=PhotoImage(file="next.png")
bglabel=Label(window)
bglabel.place(x=600,y=200)
next1() # set the first image
nextbutton1=Button(window, image=nextbutton, bd=0, command=next1)
nextbutton1.pack()
window.mainloop()
(totally untested since i don't have your images).

Call Back not running

My code :-
def load():
label.configure(text="Error")
button = tkinter.Button(main,width=8,text="Continue >", command="load")
and the window is running perfectly but the callback is not running, I have tried many kind of callbacks like printing configuring etc. but didn't worked. What is solution?
The command argument is expecting to have a function, Not a string.
Instead of string, use the the function.
Instead of:
button = tkinter.Button(main,width=8,text="Continue >", command="load")
write:
button = tkinter.Button(main,width=8,text="Continue >", command=load)
In your code you passed load as a string .So it doesn't work. Because command in the sense what command should be performed when the component Button is clicked.
So try to change it don't pass it as a string .
Change it as
button = tkinter.Button(main,width=8,text="Continue >", command=load)

Python - subprocess error

I have a button at my window. If I cklick it, I want to start the VLC and stream an URL.
def startstream():
args = ['C:/Program Files/VideoLAN/VLC/vlc.exe', 'http://dreambox:8001/1:0:19:7B:B:85:C00000:0:0:0:']
subprocess.call(args)
# Buttons
button_tnt = Button(fenster, text = "TNT Serie HD", command = startstream)
This works as I want.
The following one doesn't work like I want it and I have no idea why not.
def startstream(url):
args = ['C:/Program Files/VideoLAN/VLC/vlc.exe', url]
subprocess.call(args)
# Buttons
button_tnt = Button(fenster, text = "TNT Serie HD", command = startstream('http://dreambox:8001/1:0:19:7B:B:85:C00000:0:0:0:'))
With the first code, the window appears and nothing happen. If I click the button, the stream starts, perfect.
Second code: I run the script and the stream starts immediatelly. After I close the VLC I cannot reopen the stream over the button, it has no function.
But I want to use the second code. I have more than one button and so I can only change the argument for each button. With the first code I have to write a new function for every stream.
Please help me :(
Thanks!
You're executing startstream instead of assigning it*. To give it arguments, use the following:
button_tnt = Button(fenster,
text="TNT Serie HD",
command= lambda: startstream('http://dreambox:8001/1:0:19:7B:B:85:C00000:0:0:0:'))
*specifically, you're executing startstream(..) and assigning the result of that to command.
The lambda will instead create the function that command will call when clicked.
The reason is, that when constructing Button, purpose of command argument is to define a function to be called, when the button is called.
Your first example does that, it assigns name of a function to call.
In second you do not assign a function, but result of calling the startstream call. That is why it starts streaming immediately.
If you really want to assign a function using the url, you may do so this way:
from functools import partial
# Buttons
button_tnt = Button(fenster, text = "TNT Serie HD", command = partial(startstream, 'http://dreambox:8001/1:0:19:7B:B:85:C00000:0:0:0:'))
The partial will create a new function, which will call startstream with given parameter.
The trouble is that in your second example you're not passing arguments to startstream on click, you're calling startstream when you create your button.
button_tnt = Button(fenster,
text = "TNT Serie HD",
command = startstream('http://dreambox:8001/1:0:19:7B:B:85:C00000:0:0:0:'))
# ^ Invocation takes place right here.

Python tkinter button callback unexpected behaviour [duplicate]

This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 6 months ago.
I have made a simple "program launcher" in Python. I have a tab delimited text file, with, at the moment, just:
notepad c:\windows\notepad.exe
write c:\windows\write.exe
The program reads the textfile and creates an array of objects. Each object has a name property (e.g. notepad) and a route property (e.g. C:\windows\notepad.exe). Then, for each object, a button should be made with the correct name on the button, and clicking the button should execute the correct program using the route.
The program very nearly works. Indeed, the array of objects is formed correctly, because the for loop correctly prints out two different program names, and two different routes. The problem is that both buttons, although labeled correctly, launch the write program ! I believe the problem is arising somewhere in the callback, but my Python knowledge just isn't developed enough to solve this! As you can see from my code below, I have tried an "inline" callback, and with a "runprog" function defined. They both give the same outcome.
Your help would be appreciated.
import Tkinter as tk
import subprocess
class MyClass:
def __init__(self, thename,theroute):
self.thename=thename
self.theroute=theroute
myprogs = []
myfile = open('progs.txt', 'r')
for line in myfile:
segmentedLine = line.split("\t")
myprogs.append(MyClass(segmentedLine[0],segmentedLine[1]))
myfile.close()
def runprog(progroute):
print(progroute)
subprocess.call([progroute])
root = tk.Tk()
button_list=[]
for prog in myprogs:
print(prog.thename)
print(prog.theroute)
button_list.append(tk.Button(root, text=prog.thename, bg='red', command=lambda: runprog(prog.theroute)))
# button_list.append(tk.Button(root, text=prog.thename, bg='red', command= lambda: subprocess.call(prog.theroute)))
# show buttons
for button in button_list:
button.pack(side='left', padx=10)
root.mainloop()
Change your command to look like this:
tk.Button(..., command=lambda route=prog.theroute: runprog(route))
Notice how the lambda has a keyword argument where you set the default value to the route you want to associate with this button. By giving the keyword arg a default value, you are "binding" this value to this specific lambda.
Another option is to use functools.partial, which many people find a little less intimidating than lambda. With this, your button would look like this:
import functools
...
tk.Button(..., command=functools.partial(runprog,route)
A third option is to move the "runprog" function to the class instead of in the main part of your program. In that case the problem becomes much simpler because each button is tied specifically to a unique object.
tk.Button(..., command=prog.runprog)
Just change this line:
button_list.append(tk.Button(root, text=prog.thename, bg='red', command=lambda: runprog(prog.theroute)))
to:
button_list.append(tk.Button(root, text=prog.thename, bg='red',
command= (lambda route:(lambda: runprog(route))) (prog.theroute)))
Reasoning: when you create a lambda function (or any other function within a function), it does have access (in Python 2, read-only access) to the variables in the outer function scope. However, it does access the "live" variable in that scope - when the lambda is called, the value retrieved from "prog" will be whatever "prog" means at that time, which in this case will be the last "prog" on your list (since the user will only click a button long after the whole interface is built)
This change introduces an intermediary scope - another function body into which the current "prog" value is passed - and prog.theroute is assigned to the "route" variable in the moment the expression is run. That is done once for each program in the list. The inner lambda which is the actual callback does use the "route" variable in the intermediate scope - which holds a different value for each pass of the loop.

Categories