Tkinter - Linking buttons to different scripts - python

I am exploring GUI's at the moment. What I want is to have a GUI with a 2 buttons and I want each of the buttons to run a separate python script when clicked.
I have outlined my code below (the first button runs just fine but I am having issues with the second button.
Error Message when I choose the second button:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\ProgramData\Anaconda3\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
Code:
from tkinter import *
import tkinter as tk
master = Tk()
def First_Scriptcallback():
exec(open(r'Desktop\Automation\First_Script.py').read())
def second_Scriptcallback():
exec(open(r'Desktop\Automation\Second_Script.py').read())
#master.title("Test GUI")
#canvas = tk.Canvas(master, height=300, width = 400)
#canvas.pack()
firstButton = Button(master, text="Run first script", command=First_Scriptcallback)
firstButton.pack()
secondButton = Button(master, text="Run second script", command=second_Scriptcallback)
secondButton.pack()
mainloop()
Thanks

As #matiiss suggested importing the other scripts into your program can help and it can be done like this,
import First_Script as first
import Second_Script as second
from tkinter import *
import tkinter as tk
master = Tk()
def First_Scriptcallback():
first.function_name()#here you must create functions in first_script to call in this main script
def second_Scriptcallback():
second.function_name()
#master.title("Test GUI")
#canvas = tk.Canvas(master, height=300, width = 400)
#canvas.pack()
firstButton = Button(master, text="Run first script", command=First_Scriptcallback)
#command=first.function_name
#we can also directly call an function using above command,but sometimes there are problems related to this approch
firstButton.pack()
secondButton = Button(master, text="Run second script", command=second_Scriptcallback)
#command=second.function_name
secondButton.pack()
mainloop()
here for this example the scripts and the program must be in same directory.

Related

Errror: _tkinter.TclError: bad window path name ".!button"

So today I tried to use python classes for the first time, to remove the excessive use of global keyword. I am trying to create a tkinter window in which, when we click one button it removes the clicked button and replaces it with a new button. And when we click it again, it removes this button and replaces the old (first) button and this should cycle through out...
This is my code which I made:
# ================= Importing Modules ===================
from tkinter import *
import tkinter as tk
# ====================================================
class Test():
# ============= Play Button Click =============
def fun1(self):
self.hi.destroy()
self.he.place(x=350,y=340)
# ============ Pause Button Click =============
def fun2(self):
self.he.destroy()
self.hi.place(x=350,y=340)
# ============ Player Window ================
def __init__(self):
self.root = Tk()
self.root.geometry('700x400')
self.root.resizable(0,0)
self.root["bg"] = "black"
self.hi = tk.Button(self.root, text="button 1", bg="white", bd=0, command=lambda: self.fun1() , relief=RIDGE)
self.hi.place(x=350,y=340)
self.he = tk.Button(self.root, text="button 2", bg="white", bd=0, command=lambda: self.fun2() , relief=RIDGE)
self.root.mainloop()
# ============== Calling ===========
if __name__ == '__main__':
Test()
But Instead of the desired output, sadly, I got this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return self.func(*args)
File "C:/XXX/XXX/Desktop/test.py", line 29, in <lambda>
self.he = tk.Button(self.root, text="button 2", bg="white", bd=0, command=lambda: self.fun2() , relief=RIDGE)
File "C:/XXX/XXX/Desktop/test.py", line 16, in fun2
self.hi.place(x=350,y=340)
File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 2477, in place_configure
self.tk.call(
_tkinter.TclError: bad window path name ".!button"
SO MY QUESTION IS:
doubt1 = Any idea what I am doing wrong?
doubt2 = Or isn't this possible?
if doubt1 or doubt2:
Please explain it...
elif:
Please tell me a better alternative or idea, how to do this efficiently.
else:
Note: I have researched so many questions. Nothing helped me out. Especially ---|
↓
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤthis one.
You're destroying self.hi and then later trying to call place on the destroyed button. Once a widget has been destroyed you can no longer use it.
If you want to keep cycling the buttons, don't destroy them. Since you are using place, you can use self.hi.place_forget() and self.he.place_forget() to remove the buttons from view without destroying them.

New python program runs on start up and not on call

so i want to open a new python tkinter program when i click a button in the GUI. As soon as i run the code ProgramB runs. When i close this GUI the main GUI runs but when i click the button in this GUI, it doesn't open ProgramB. I'm Learning pyhton and tkinter and don't know how to fix this problem. Thank you in advance.
My main code:
from tkinter import *
from tkinter.ttk import *
import basis_tkinter
import subprocess
mainwindow = Tk()
mainwindow.title= "test opening new file"
def open():
subprocess.call("basis_tkinter.py", shell=True)
btnopen = Button(mainwindow, text="open new program", command=lambda: open).pack()
mainwindow.mainloop()
Program B (basis_tkinter):
from tkinter import *
MainWindow = Tk()
MainWindow.title("Title")
MainWindow.iconbitmap(r'C:\Users\Gebruiker\Documents\school 2020-2021\stage\interface\Afbeeldingen\Apex icon.ico')
Background= Label(MainWindow, image=PhotoImage(file=r"C:\Users\Gebruiker\Documents\school 2020-2021\stage\interface\Afbeeldingen\Apex logo.png"))
Background.place(x=0,y=0, relwidth=1, relheight=1)
MainWindow.geometry("400x400")
def clicked():
label = Label(MainWindow, text="You clicked").pack()
button = Button(MainWindow, text="Click me", command=clicked).pack()
MainWindow.mainloop()

Command in the button executes without showing the button widget.(Python tkinter)

(Attempt 1): Command in the button executes without showing the button widget. (I would like the user click browse button to select the file)
(Attempt 2): If I use lambda then the next chunk of code immediately after the button gets executed throwing error. As the merge_doc = MailMerge(file_name) gets its file_name only after the button command gets executed. Please suggest.
Attempt 1
from tkinter import *
from tkinter import ttk
from mailmerge import MailMerge
import tkinter as tk
import os
from tkinter import filedialog
import tkinter.font as font
root = tk.Tk()
root.geometry("")
root.title("Test")
file_name=""
def main():
global file_name
file = filedialog.askopenfile(initialdir="./")
if file:
file_name=file.name
browse_button = Button(root, text ='BROWSE',command=main())
browse_button.grid(row=1, column=0, padx=10, ipadx=25,ipady=35)
browse_button.grid_forget()
merge_doc = MailMerge(file_name)
Attempt 2
from tkinter import *
from tkinter import ttk
from mailmerge import MailMerge
import tkinter as tk
import os
from tkinter import filedialog
import tkinter.font as font
root = tk.Tk()
root.geometry("")
root.title("Test")
file_name=""
def main():
global file_name
file = filedialog.askopenfile(initialdir="./")
if file:
file_name=file.name
browse_button = Button(root, text ='BROWSE',command=lambda:main())
browse_button.grid(row=1, column=0, padx=10, ipadx=25,ipady=35)
browse_button.grid_forget()
merge_doc = MailMerge(file_name)
Error thrown during Attempt 2:
Traceback (most recent call last):
File "C:\Users\Rocky\Desktop\TEST\Testnew.py", line 30, in <module>
merge_doc = MailMerge(file_name)
File "C:\Python38\lib\site-packages\mailmerge.py", line 25, in __init__
self.zip = ZipFile(file)
File "C:\Python38\lib\zipfile.py", line 1251, in __init__
self.fp = io.open(file, filemode)
FileNotFoundError: [Errno 2] No such file or directory: ''
If you want user to click the BROWSE button to select file, then you can use wait_variable():
import tkinter as tk
from tkinter import filedialog
from mailmerge import MailMerge
root = tk.Tk()
filename = tk.StringVar()
def main():
file = filedialog.askopenfilename(initialdir='./')
filename.set(file)
browse_button = tk.Button(root, text='BROWSE', command=main)
browse_button.grid(row=1, column=0, padx=10, ipadx=25, ipady=35)
root.wait_variable(filename) # wait for filename to be updated
browse_button.grid_forget()
# should cater empty filename (user click Cancel in file dialog)
merge_doc = MailMerge(filename.get())
...
Note that you need to cater when user click Cancel button in the file selection dialog.
According to your 1st attempt, you make a button and grid it as:
browse_button = Button(root, text ='BROWSE',command=main())
browse_button.grid(row=1, column=0, padx=10, ipadx=25,ipady=35)
But then you call grid_forget() function which is vanishing the button from tkinter window.
browse_button.grid_forget()
After all, you have to add root.mainloop() to run the window infinitely.
And change
browse_button = Button(root, text ='BROWSE',command=main())
to
browse_button = Button(root, text ='BROWSE',command=main)
On second attempt, put the merge_doc = MailMerge(file_name) statement inside if file: block on main().
Because, when python reads the code, initially the file_name variable contains "". So, before pressing any button, it calls merge_doc = MailMerge(file_name) where file_name is "". So, if you want to call the method after choosing the file, put it inside the if block.

Preventing circular import example

Lets say I have two python files. Both with an GUI. First is "Main" second is "Calculator". From Main I will start Calculator. So I have to import calculator. In Calculator I will do a calculation. Lets keep I easy an say 1+1=2. Now I want to "send" this Result to an Text in Main.
How do I do that without an circular import? I cant find an good tutorial/example for that!
My code so far:
Main:
from tkinter import *
import Test_2
window = Tk()
window.title("First Window")
def start():
Test_2.start_second()
Input1 = Entry(window)
Input1.grid(row=0,column=0, padx=10, pady=5)
Start = Button(window,text="Start", command=start)
Start.grid(row=1,column=0, padx=10, pady=5)
window.mainloop()
Second:
from tkinter import *
def start_second():
window2 = Tk()
window2.title("Second Window")
def send():
x = Input.get()
Input2 = Entry(window2)
Input2.grid(row=0,column=0, padx=10, pady=5)
Send = Button(window2,text="Send", command=send)
Send.grid(row=1,column=0, padx=10, pady=5)
window2.mainloop()
This code does exactly what you asked for (as opposed to what I suggested in the comment; but anyway, you either get a value from a module function or you send a reference for it to alter)
I tried to follow your structure.
Basically it is a matter of sending the parent window and the first entry as parameters to the second window creation function. Don't call mainloop two times, just once in the end, and use Toplevel for all other windows after the main Tk one. This is not to say that I like the use of an inner function and of the lambda, for readability, but lambdas are necessary in tkinter everytime you want to send parameters to a command callback, otherwise it will get called right way in command definition.
tkinter_w1.py (your main.py)
from tkinter import Tk, ttk
import tkinter as tk
from tkinter_w2 import open_window_2
root = Tk()
entry1 = ttk.Entry(root)
button1 = ttk.Button(root, text='Open Window 2',
command=lambda parent=root, entry=entry1:open_window_2(parent, entry))
entry1.pack()
button1.pack()
root.mainloop()
tkinter_w2.py (your Test_2.py)
from tkinter import Tk, ttk, Toplevel
import tkinter as tk
def open_window_2(parent, entry):
def send():
entry.delete(0,tk.END)
entry.insert(0,entry2.get())
window2 = Toplevel(parent)
entry2 = ttk.Entry(window2)
button2 = ttk.Button(window2, text='Send', command=send)
entry2.pack()
button2.pack()

Python 3.2, Tkinter class declarations, class undefined when subclassing

I'm learning python and encountering issues with what appears to be class decorators from tkinter. I can get tkinter to work as long as I never try to decorate with Frame. The code below gives me no errors, and no window. For whatever reason, the class I define, never gets defined. Below is my code. Below that is testing i've done based on similar questions regarding tkinter.
>>> from tkinter import *
import tkinter
class Apples:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="Quit", fg="blue", command=frame.quit)
self.button.pack(side=LEFT)
self.hellos = Button(frame, text="Hello", command=self.say_hello)
self.hellos.pack(side=LEFT)
def say_hello(self):
print("Hello World!")
root = Tk()
app = Apples(root)
root.mainloop()
No window shows up. No error. So I check on the class...
>>> test = Apples(root)
Traceback (most recent call last):
File "<pyshell#54>", line 1, in <module>
test = Apples(root)
NameError: name 'Apples' is not defined
>>>
I found a similar question Creating buttons with Python GUI and attempted the code from pythonMan. I still am encountering the same class declaration issue.
>>> from tkinter import *
class Application(Frame):
"""A GUI application with three button"""
def __init__(self,master):
self.master = master
self.create_widgets()
def create_widgets(self):
#"""Create three buttons"""
#Create first buttom
btn1 = Button(self.master, text = "I do nothing")
btn1.pack()
#Create second button
btn2 = Button(self.master, text = "T do nothing as well")
btn2.pack()
#Create third button
btn3=Button(self.master, text = "I do nothing as well as well")
btn3.pack()
root = Tk()
root.title("Lazy Button 2")
root.geometry("500x500")
app = Application(root)
root.mainloop()
>>> Application
Traceback (most recent call last):
File "<pyshell#58>", line 1, in <module>
Application
NameError: name 'Application' is not defined
>>>
I can think something is not being declared properly, but is not generating a syntax error. Any help would be greatly appreciated.
I think you don't need an ">>>" sign.
"from Tkinter import *"
Assuming your code is properly reproduced in the second part of your question, you're defining your class incorrectly. You have def __init__ at the same level of indentation as class Application(Frame). Your methods need to be indented for them to be part of the class.
The code in the first part of your question works fine for me.

Categories

Resources