I am working on a project that uses buttons, but I would like to make it modular. The only problem I can't seem to get pass by is getting if someone is pressing the button from a different file
-file 1
from tkinter import*
class app2:
def __init__(self):
s = Tk()
but = Button(text="Test",command=subcommand)
but.pack()
s.mainloop()
def subcommand():
x = command()
x.command
class command:
def __init__(self,int):
self.command = ()
y = app2()
file 2-
from tkinter import*
from idea2 import *
def clicked():
print("HI")
x = command()
x.command(clicked())
My code basically just takes a function from another file and loads it into the command class. So basically the button can get different commands from different files. I don't know what to put for "x = command(???)" because I am trying to get that from a different file.
Basic question:
How to make my Tkinter button modular.
just pass the callback into the app2 constructor
file1
from tkinter import*
class app2:
def __init__(self,btn_callback=subcommand):
s = Tk()
but = Button(text="Test",command=btn_callback)
but.pack()
s.mainloop()
file2
from idea import app2
def my_callback(*args,**kwargs):
print("Buttton pressed...")
app = app2(my_callback)
or better yet make use of the event system in tkinter ... something like this
import tkinter as Tkinter
import tkinter.messagebox as tkMessageBox
def helloCallBack(*a,**kw):
tkMessageBox.showinfo( "Hello Python", "Hello World")
class MyApp(Tkinter.Tk):
def __init__(self):
super().__init__()
self.b = Tkinter.Button(self, text ="Hello", command = lambda:self.event_generate("<<button_click>>"))
self.b.pack()
app = MyApp()
app.bind("<<button_click>>",helloCallBack)
app.mainloop()
Related
I'm trying to keep my code clean by separating the GUI from the logic.
Inside of the 'main.py' file, I'd like to call functions from other files that are imported to build the GUI.
The problem is that I cannot figure out how to build a GUI from the 'main.py' file when I try to call another file as an imported module.
Here's what I have in the 'main.py' file:
from tkinter import *
import create_btn
class Main(Tk):
def __init__(self):
super().__init__()
self.title('Main Window')
self.geometry('600x400')
self.eval('tk::PlaceWindow . center')
if __name__ == '__main__':
app = Main()
app.mainloop()
And here is what I have in the 'create_btn.py' file:
from tkinter import *
def createBTN(self):
self.b1 = Button(root, text='B1')
self.b1.pack()
So, how exactly can I build a simple button from another file that I want to import into the 'main.py', or in other words, how do I get the 'create_btn.py' file to build the button inside of the 'main.py' file? A simple example would be greatly appreciated.
I have completely rewritten your code:
# main.py
from tkinter import *
import create_btn
class Main_app:
def __init__(self):
self.root = Tk()
self.root.title("Main Window")
self.geometry("600x400")
self.eval('tk::PlaceWindow . center')
self.button = create_btn.create(self.root)
self.root.mainloop()
a = Main_app()
# create_btn.py
from tkinter import *
class create:
def __init__(self, root):
btn = Button(root, text="Button")
btn.pack()
return btn
This way you can edit the button later in main.py
I'm pretty new in Python. I'm trying to develop a GUI using a drag and drop designer called PAGE (http://page.sourceforge.net/html/index.html) based on Tkinter.
After having created a new project, I added a button to a window. The following code is autogenerated by PAGE and saved into a file called 'myGui.py':
import sys
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.constants import *
import myGui_support
class Toplevel1:
def __init__(self, top=None):
...
self.StartButton = tk.Button(self.top)
self.StartButton.configure(command=myGui_support.StartButton_Callback)
...
def start_up():
myGui_support.main()
if __name__ == '__main__':
myGui_support.main()
PAGE creates also a second file called 'myGui_support.py', in which after some import statements, the following code is included:
import myGui
def main(*args):
'''Main entry point for the application.'''
global root
root = tk.Tk()
root.protocol( 'WM_DELETE_WINDOW' , root.destroy)
# Creates a toplevel widget.
global _top1, _w1
_top1 = root
_w1 = myGui.Toplevel1(_top1)
root.mainloop()
if __name__ == '__main__':
myGui.start_up()
def StartButton_CallBack():
_w1.DebugLabel["text"] = 'START BUTTON PRESSED'
def StopButton_CallBack():
_w1.DebugLabel["text"] = 'STOP BUTTON PRESSED'
Once I run the myGui in Spyder, an Attribute error occurres:
AttributeError: module 'myGui_support' has no attribute 'StartButton_Callback'
How can it be possible? I already defined the callback function into the 'myGui_support.py'. Any suggestions?
I am trying to bring the Tkinter script to the testing. The application would be tested without opening the window, just simulate the button click. What I've done seems to be inappropriate, so how could I pass the instance properly? Thanks.
Here is the prototype of my code:
# importing Tkinter and math
from tkinter import *
import math
class calc:
def __init__(self,master):
"""Constructor method"""
master.title('Calculator')
master.geometry()
self.master = master
self.btn_equal = Button(self.master,text="=",width=11,height=3, fg="red", bg="light green",command=lambda:self.equals()).grid(row=4, column=4,columnspan=2)
self.e.grid(row=0,column=0,columnspan=6,pady=3)
def start_application():
root = Tk()
app = calc(root)
# print(app)
root.bind_class("Button", "<Button-1>", app.callback)
return root
if __name__ == "__main__":
start_application().mainloop()
Here is the testing code:
import unittest
from tkinter import *
from calculator2 import start_application
class TestCalculator2(unittest.TestCase):
# start the application, test, and destroy at the end of the test
async def _start_app(self):
self.app.mainloop()
def setUp(self):
self.app = start_application()
self._start_app()
def tearDown(self):
self.app.destroy()
class TestCalculation(TestCalculator2):
def test_startup(self):
title = self.app.winfo_toplevel().title()
expected = "Calculator"
self.assertEqual(title, expected)
def test_addition(self):
self.btn_equal.invoke() # _Tkinter.tkapp has no attribute, since root is not calc object
The short answer is that the Button widget calls the grid, leading to the NoneType. The correct way of instantiating a button inside a class with position should be:
self.btn_equal = Button(self.master,text="=",width=11,height=3, fg="red", bg="light green",command=lambda:self.equals())
self.btn_equal.grid(row=4, column=4,columnspan=2)
On the other hand, the correct way to instantiate the calc class mentioned above and write a successful unit test with invoke(), which clicks the button. The code won't open the GUI, and the testing script works (with warning):
class TestCalculator2(unittest.TestCase):
# start the application, test, and destroy at the end of the test
async def _start_app(self):
self.app.mainloop() # the root in Tkinter activates the mainloop()
def setUp(self):
self.app = start_application() # activate the root
self.calc = calc(self.app) # instantiate the calculator
self._start_app()
def tearDown(self):
self.app.destroy()
class TestCalculation(TestCalculator2):
def test_startup(self):
title = self.app.winfo_toplevel().title()
expected = "Calculator"
self.assertEqual(title, expected)
def test_addition(self):
self.calc.btn_AC.invoke() # click AC to clear the place
self.calc.btn_7.invoke() # click button 7
self.calc.btn_plus.invoke() # click button +
self.calc.btn_5.invoke() # click button 5
result = self.calc.btn_equal.invoke() # click button equal, get the value
self.assertEqual(result, 12)
Note that the code snippet just gives a brief idea of how unit testing works on Tkinter, the calc class provided is incomplete.
I've got this Tkinter thing going with Python 3.5. I'm trying to pass the contents of an entry-box over to a different module so I can do something else with it. I'm making the Tkinter window like this:
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Label(self,text="Testers Name").grid()
self.testers_name = tk.Entry(self,text="Testers Name").grid()
tk.Label(self,text="Report Name").grid()
self.report_name = tk.Entry(self,text="Report Name").grid()
submitButton = tk.Button(self, text="Test Selected",command=self.query_checkbuttons)
submitButton.grid()
def query_checkbuttons(self):
some_stuff_blah_blah_blah
if __name__ == "__main__":
gui = GUI()
print(gui.report_name)
gui.mainloop()
So, that works OK. At least the print call in __main__ reports None and not an error.
I have another module called pdf.py and that has a call in it that fails when I try to pull in report_name like this:
def myFirstPage(canvas, doc):
import gui
print(gui.report_name)
canvas.saveState()
canvas.setFont('Times-Bold',16)
canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title)
canvas.setFont('Times-Roman',9)
canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo)
canvas.restoreState()
I've tried everything I can think of. Let's see, how about:
print(gui.gui.report_name)
print(gui.report_name)
print(gui.report_name.get())
print(gui.GUI.report_name)
print(gui.GUI().report_name)
print(gui.GUI().report_name.get())
I've made gui global in and out of the __main__ call.
I've done this in various places in pdf.py:
import gui
from gui import *
from gui import gui
from gui import GUI
in conjunction with permutations of the last slew of calls...Nothing works.
I can print it find in its own module, but I can't get it to show up outside of there. How do I do this?
Not entirely sure but I guess that your issue is related to the following line
if __name__ == "__main__":
meaning that your class GUI is not instantiated as can be seen in [0]. Moving the creation of the GUI class, that is using the following code snippet
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Label(self,text="Testers Name").grid()
self.testers_name = tk.Entry(self,text="Testers Name").grid()
tk.Label(self,text="Report Name").grid()
self.report_name = tk.Entry(self,text="Report Name").grid()
submitButton = tk.Button(self, text="Test Selected",command=self.query_checkbuttons)
submitButton.grid()
def query_checkbuttons(self):
some_stuff_blah_blah_blah
gui = GUI()
print(gui.report_name)
gui.mainloop()
will at least allow you to import the variable gui from that module.
However, I strongly recommand to not instantiate the class at the end of the module because it is reinstantiated every time your import that module!
EDIT1:
I would do it as follows:
thiscode.py
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Label(self,text="Testers Name").grid()
self.testers_name = tk.Entry(self,text="Testers Name").grid()
tk.Label(self,text="Report Name").grid()
self.report_name = tk.Entry(self,text="Report Name").grid()
submitButton = tk.Button(self, text="Test Selected",command=self.query_checkbuttons)
submitButton.grid()
def query_checkbuttons(self):
some_stuff_blah_blah_blah
def start_gui():
gui = GUI()
# print(gui.report_name)
gui.mainloop()
if __name__ == "__main__":
start_gui()
and then in pdf.py
def myFirstPage(canvas, doc):
import gui
gui.start_gui() # --> manually open up the gui after import <--
# print(gui.report_name)
canvas.saveState()
canvas.setFont('Times-Bold',16)
canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title)
canvas.setFont('Times-Roman',9)
canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo)
canvas.restoreState()
Now let me clarify that when you call thiscode.py via python thiscode.py you will end up with exactly one GUI being opened up. This is however not the case if you import the module as can be seen in pdf.py. Hence, we directly instantiate one GUI object via function invocation which must be done manually (see line 3 with the unmistakable inline comment in pdf.py).
[0] https://docs.python.org/3/tutorial/modules.html#executing-modules-as-scripts
The whole point of the if __name__ block is to only execute its contents when that file is run directly, and not when it is imported. So if you do want to import the gui instance, you should not create it within that block.
I learned the book "programming python' these days. When I run the examples, I met the problem. The shell showed me the error:
AttributeError: 'NoneType' object has no attribute 'pack'
However, I copy the exactly code from the book. I'm a freshman of Python. I try to fix it by myself, but I failed. So I hope anyone could kindly help me.
Thanks !!!!!!
CODEļ¼
#File test.py
from tkinter import *
from tkinter.messagebox import showinfo
def MyGui(Frame):
def __init__(self, parent = None):
Frame.__init__(self, parent)
button = Button(self, text='press', command=reply)
button.pack()
def reply(self):
showinfo(title = 'popup',message ='Button pressed!')
if __name__ == '__main__':
window = MyGui()
window.pack()
window.mainloop()
#File test2.py
from tkinter import *
from test import MyGui
mainwin = Tk()
Label(mainwin,text = __name__).pack()
popup = Toplevel()
Label(popup,text = 'Attach').pack(side = LEFT)
MyGui(popup).pack(side=RIGHT)
mainwin.mainloop()
You can fix this with the following code:
#File test.py
from tkinter import *
from tkinter.messagebox import showinfo
class MyGui(Frame):
def __init__(self, parent = None):
Frame.__init__(self, parent)
button = Button(self, text='press', command=self.reply)
button.pack()
def reply(self):
showinfo(title = 'popup',message ='Button pressed!')
if __name__ == '__main__':
window = MyGui()
window.pack()
window.mainloop()
Basically two small syntax errors. First you were trying to make a class of MyGui, but you used keyword def which made a function instead (that returned None, hence the error you received.) It is syntaxically correct in python to define functions inside of functions so it was a little harder to catch. You have to use the keyword class to define a class.
Secondly when referencing the function reply you must use self.reply within the class itself.