Python - subprocess error - python

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.

Related

Generate buttons in Tkinter with a different name each time

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())

How do I pass parameters to a function from a guizero object?

I am unable to pass any parameter to a function called by a guizero widget either with the attribute "command" at initialization or by invoking an event.
This works as expected (no parameters passed):
from guizero import App, PushButton
def go():
print (10)
app = App(title="Test")
button = PushButton(app, text = "Click Here", command = go)
app.display()
but the following prints the number 10 once, before the button is clicked and then when I click, nothing happens
from guizero import App, PushButton
def go(n):
print (n)
app = App(title="Test")
button = PushButton(app, text = "Click Here", command = go(10))
app.display()
The same result I get with this:
from guizero import App, PushButton
def go(n):
print (n)
app = App(title="Test")
button = PushButton(app, text = "Click Here")
button.when_clicked = go(10)
app.display()
What am I missing?
Thanks in advance!
from guizero import App, PushButton
def go(n):
print (n)
app = App(title="Test")
button = PushButton(app, text = "Click Here", command = lambda: go(10))
app.display()
Whenever you write go(10) anywhere, you are invoking the function go. You might think you are passing go with arguments, but you aren't because the parentheses next to go() invoke the function right then and there. If you want to pass the function go to another function, and go should also be passed with some arguments, then you need to wrap the function go and pass the wrapped function as the argument "command". Using a lambda function to wrap go(10) is one such way of doing this.
The reason this works is that the lambda function is NOT invoked right then and there. You are saying that that command() should invoke the declared anonymous lambda function eventually, and when that lambda function is called it will itself call go(10). You are declaring an anonymous lambda function, NOT invoking it. The lambda function will be invoked later on as command().
In all GUI frameworks command= (or similar) expects callback - it means function's name without () and without parameters.
If you use go(10) then it does
result = go(10)
PushButton( ...., command=result)
so it executes go(10) before you even click button - and it assign result to command=. Because go() returns None so you get PushButton( ...., command=None) and when you click button then you get nothing.
You have to create new function which you can run without ()
def go_10():
go(10)
PushButton( ...., command=go_10)
Or you can use lambda to create function directly in command=
PushButton( ...., command=lambda:go(10))
I found the solution!
Using a lambda function is a clever way to work around the callback limitation but I found a more proper way to pass arguments to a function by using the widget properties (pun not intended). As it turns out I missed a very brief reference to the property args that can be set at instantiation and is used to pass a list to the function called by command
So here is my solution:
from guizero import App, PushButton
def go(n):
print (n)
app = App(title="Test")
button = PushButton(app, text = "Click Here", args = [10], command = go)
app.display()
As expected, it prints '10' every time you click the button and not before.
Thanks for all the other answers!

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).

Python TK Notebook tab change check

Newbie programmer here. I am building a tk based desktop app and ran into an issue:
I have a main window with several stuff in it including two tabs:
global nBook
nBook = ttk.Notebook(self, name="book")
nBook.place(x=300,y=400)
frameOne = ttk.Frame(nBook, width=100, height=100)
frameTwo = ttk.Frame(nBook, width=100, height=100)
nBook.add(frameOne, text='T1')
nBook.add(frameTwo, text='T2')
frameOne.bind("<<NotebookTabChanged>>", self.routine())
frameTwo.bind("<<NotebookTabChanged>>", self.routine())
routine() is a function that SHOULD perform a check every time T2 is selected
def routine(self):
if str(nBook.index(nBook.select())) == "2":
# Do stuff
else:
pass
Problem is that it doesn't do anything when the tab is changed except for calling the routine function as soon as I open the app and never again. I just can't figure out what I'm doing wrong.
Could anyone point out the mistake(s) I'm making?
EDIT: Same issue if I try
nBook.bind("<<NotebookTabChanged>>", self.xbRoutine())
The error comes from the event binding statements: when using self.routine() the callback is called when the bind statement is executed, not when the event is triggered. To get the correct behavior, the second argument of bind should be the name of a function not a call to this function, so simply remove the parentheses.
Another error: when using bind, the callback function is expected to have a first argument (traditionnaly called event) storing the event parameters. So you should define your callback as:
def routine(self, event):
...
I had the same problem. The answer given by #sciroccorics is not complete.
What you bind is not the tab itself, but the notebook.
So, it should be
nBook.bind("<<NotebookTabChanged>>", self.xbRoutine)
Alternatively you could use lambda.
In your case this will look something like this:
frameOne.bind("<<NotebookTabChanged>>", lambda _: self.routine())
Don't forget the _, otherwise you will get a TypeError, since the event is passed as an argument.
lamba is really helpful if your function requires one or more arguments.

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)

Categories