How can I access multiple variables containing user inputs from Entry widgets from one script and use it in another function in another script? I can find information on how to do this with one input, but nothing regarding multiple ones.
For example, say I have one script:
Main.py
from tkinter import *
import main
root = Tk()
entry = Entry(root)
entry.pack()
btn = Button(root, text='', command=lambda: main.showInput(entry.get()))
btn.pack()
root.mainloop()
class.py
def showInput(x):
print(x)
will work and print out the value. If I have multiple entries I could change the function to show
def showInput(x, y):
print(x)
print(y)
and make the corrections adjustments in the button widget on main.py. However, this isn't practical as I add more and more entries - especially since I'd like to make this work with docxtpl after I figure out this issue. What would the best way to handle this be?
I have created two frames using Tkinter. In one of the frames I am trying to add a button using a grid. When I run the program, there is no output. Instead it just freezes and I have to kill the process.
Here is the code:
from Tkinter import *
window=Tk()
window.title("calculator")
window.geometry("500x500")
window.resizable(0,0)
input_field=StringVar()
display_frame=Frame(window).pack(side="top")
button_frame=Frame(window).pack(side="bottom")
text=Entry(display_frame,font=('arial',20,'bold'),textvariable=input_field,justify="right").pack(fill="x",ipady=10)
clear_button=Button(button_frame,text="C").grid(row=0)
window.mainloop()
However, if I change the clear_button variable as
clear_button=Button(button_frame,text="C").pack()
I get an output. What am I missing here?
You cannot mix grid and pack inside the same container (a Frame/Window).
That said you should realize that your display_frame and button_frame variables are actually None! Why, because Frame(Window) will return a Frame object, but you have applied the pack() function just after that whose return value is None.
So basically the Entry and the Button widgets that you created have master=None and that means they are not inside the Frames that you defined, but actually part of the main Window.
Now you can easily see why clear_button=Button(button_frame,text="C").pack() was working as now the main window has only one geometry manager namely pack.
Here is the working code.
from tkinter import * # "Tkinter" on python 2
window=Tk()
window.title("calculator")
window.geometry("500x500")
window.resizable(0,0)
input_field=StringVar()
display_frame=Frame(window)
display_frame.pack(side="top")
button_frame=Frame(window)
button_frame.pack(side="bottom")
Entry(display_frame,font=('arial',20,'bold'),textvariable=input_field,justify="right").pack(fill="x",ipady=10)
Button(button_frame, text="C").grid(row=0)
window.mainloop()
You cannot use both grid and pack method on widgets having same master.
Here, follow the below thread for a detailed understanding :
python pack() and grid() methods together
Defining a font inside a function and in the main body of the script seems to behave differently, and I can't seem to figure out how it's supposed to work.
For example, the Label in this example ends up being in a larger font, as expected:
from Tkinter import *
from ttk import *
import tkFont
root = Tk()
default = tkFont.Font(root=root, name="TkTextFont", exists=True)
large = default.copy()
large.config(size=36)
style = Style(root)
style.configure("Large.TLabel", font=large)
root.title("Font Test")
main_frame = Frame(root)
Label(main_frame, text="Large Font", style="Large.TLabel").pack()
main_frame.pack()
root.mainloop()
However, if I try to define styles inside a function, it seems like the font gets deleted or garbage collected and is not available by the time the widget needs to use it:
from Tkinter import *
from ttk import *
import tkFont
def define_styles(root):
default = tkFont.Font(root=root, name="TkTextFont", exists=True)
large = default.copy()
large.config(size=36)
style = Style(root)
style.configure("Large.TLabel", font=large)
root = Tk()
root.title("Font Test")
define_styles(root)
main_frame = Frame(root)
Label(main_frame, text="Large Font", style="Large.TLabel").grid(row=0, column=0)
main_frame.pack()
root.mainloop()
Printing out tkFont.names() in the first version just before the main_frame.pack() lists the custom font as font<id>, but printing the same in the second version does not list the custom font outside the define_styles function. Do I have to do something special to save them?
Why can't I put that code in a function? Am I fundamentally misunderstanding something about how Fonts are supposed to be used? tkFont seems to have some kind of font registry, why aren't mine sticking around?
I have no evidence to back this up, but I believe that your large Font object is being garbage collected by Python once define_styles ends. This is because no pure Python objects have any references to it, even though the underlying Tcl implementation is still using it. This is a problem that afflicts Tkinter's PhotoImage class, as well.
The workaround is to keep the object alive by making a long-lived reference to it. Just assign it to any old attribute on the root object, for example.
def define_styles(root):
default = tkFont.Font(root=root, name="TkTextFont", exists=True)
large = default.copy()
large.config(size=36)
style = Style(root)
style.configure("Large.TLabel", font=large)
root.myfont = large
Result:
I'm new to programming, Python, this website, and actually using these kinds of websites in general, so hear me out.
I've been writing a module for a larger program using the tkinter module and ttk module, and when I import my own module into the main program, for some reason none of the ttk stuff works as it should. I mean, it appears, but the style I've written for it (s=ttk.Style(); s.configure...etc.) doesn't change it in anyway. When I run the module on its own, everything works fine. When it's imported into the main program, it just doesn't.
Not only this, but when using entry boxes, I've only just discovered that the way I'd been told to use them, with, for example, var=StringVar() as the textvariable (which again works fine when the module is run on its own), now just leaves the variable var as empty when var.get() is called. Now I've sorted this by just removing all mention of StringVar() (wish I'd known how redundant these really are), but I'd still like to know why importing them in to the main program causes them to malfunction so badly. I would give you some sample code but there's so much I'd struggle to be selective enough...
I'd appreciate any guidance you can offer.
EDIT: Would giving you something like this have helped?
stackoverflowmodule.py
import sys
from tkinter import *
from tkinter import ttk
import time
from random import randint, choice
class Decimals():
def Question1(self):
DECFrame.destroy()
frame1=ttk.Frame(DECmaster, height=height, width=width, style="NewFrame.TFrame")
frame1.pack()
Q1Label=ttk.Label(frame1, text="Question 1:", style="TitleLabel.TLabel")
Q1Label.grid(column=0, row=0, pady=(50,0))
answer=StringVar()
entry1=ttk.Entry(frame1, textvariable=answer)
entry1.grid(column=0, row=1, pady=(200,0))
# Typing in Hello should give a correct answer.
def Question1Attempt():
attempt=answer.get()
if attempt!="Hello":
print("Incorrect")
else:
print("Correct")
button=ttk.Button(frame1, text="Ok", command=Question1Attempt)
button.grid(column=0, row=2, pady=(30,0))
def Start():
global DECmaster
global s
global DECFrame
global DEC
global width
global height
DECmaster = Tk()
width=str(1000)
height=str(800)
x1=str(0)
y1=str(0)
DECmaster.geometry(width+"x"+height+"+"+x1+"+"+y1)
DECmaster.configure(bg="#8afff0")
s=ttk.Style()
s.configure("NewFrame.TFrame", background="#8afff0")
s.configure("TitleLabel.TLabel", foreground= "blue", background="#8afff0")
DECFrame=ttk.Frame(DECmaster, style="NewFrame.TFrame")
DECFrame.pack()
TitleLabel=ttk.Label(DECFrame, text="Test for Decimals", style="TitleLabel.TLabel")
TitleLabel.grid(column=1, row=0, pady=(50,0), sticky=N)
DEC=Decimals()
button=ttk.Button(DECFrame, text="Start", command=DEC.Question1)
button.grid(column=2, row=2, pady=(200,0), sticky=N)
DECmaster.mainloop()
stackoverflowprogram.py
from tkinter import *
from tkinter import ttk
import time
import stackoverflowmodule
root = Tk()
width=str(1000)
height=str(800)
x1=str(0)
y1=str(0)
##width=str(1228)
##height=str(690)
##x1=str(-1)
##y1=str(-22)
root.geometry(width+"x"+height+"+"+x1+"+"+y1)
root.configure(bg="#8afff0")
s=ttk.Style()
s.configure("NewFrame.TFrame", background="#8afff0")
s.configure("TitleLabel.TLabel", foreground= "blue", background="#8afff0")
Testframe=ttk.Frame(root, height=height, width=width, style="NewFrame.TFrame")
Testframe.pack()
Titlelabel=ttk.Label(Testframe, text="Start Test:", style="TitleLabel.TLabel")
Titlelabel.grid(column=0, row=0, pady=(50,0))
def StartTest():
stackoverflowmodule.Start()
button=ttk.Button(Testframe, text="Start", command=StartTest)
button.grid(column=0, row=1, pady=(100,0))
root.mainloop()
I realise there's an awful lot there, but I couldn't really demonstrate my point without it all. Thanks again.
The root of your problem is that you're creating more than once instance of Tk. A Tkinter app can only have a single instance of of the Tk class, and you must call mainloop exactly once. If you need additional windows you should create instances of Toplevel (http://effbot.org/tkinterbook/toplevel.htm).
If you want to create modules with reusable code, have your modules create subclasses of Frame (or Toplevel if you're creating dialos). Then, your main script will create an instance of Tk, and place these frames in the main window or in subwindows.
If you want to sometimes use your module as a reusable component and sometimes as a runnable program, put the "runnable program" part inside a special if statement:
# module1.py
import Tkinter as tk
class Module1(tk.Frame):
def __init__(self, *args, **kwargs):
label = tk.Label(self, text="I am module 1")
label.pack(side="top", fill="both", expand=True)
# this code will not run if this module is imported
if __name__ == "__main__":
root = tk.Tk()
m1 = Module1(root)
m1.pack(side="top", fill="both", expand=True)
In the above code, if you run it like python module1.py, the code in that final if statement will run. It will create a root window, create an instance of your frame, and make that frame fill the main window.
If, however, you import the above code into another program, the code in the if statement will not run, so you don't get more than one instance of Tk.
Let's assume you have two modules like the above, and want to write a program that uses them, and each should go in a separate window. You can do that by writing a third script that uses them both:
# main.py
import Tkinter as tk
from module1 import Module1
from module2 import Module2
# create the main window; every Tkinter app needs
# exactly one instance of this class
root = tk.Tk()
m1 = Module1(root)
m1.pack(side="top", fill="both", expand=True)
# create a second window
second = tk.Toplevel(root)
m2 = Module2(second)
m2.pack(side="top", fill="both", expand=True)
# run the event loop
root.mainloop()
With the above, you have code in two modules that can be used in three ways: as standalone programs, as separate frames within a single window, or as separate frames within separate windows.
You can't create two instances of tkinter.Tk. If you do, one of two things will happen.
Most of the code in the script may just not run, because it's waiting for the module's mainloop to finish, which doesn't happen until you quit.
If you structure things differently, you'll end up with two Tk instances, only one of which is actually running. Some of the code in your script will happen to find the right Tk instance (or the right actual Tk objects under the covers), because there's a lot of shared global stuff that just assumes there's one Tk "somewhere or other" and manages to find. But other code will find the wrong one, and just have no effect. Or, occasionally, things will have the wrong effect, or cause a crash, or who knows what.
You need to put the top-level application in one place, either the module or the script that uses it, and have the other place access it from there.
One way to do this is to write the module in such a way that its code can be called with a Tk instance. Then, use the __main__ trick so that, if you run the module directly as a script (rather than importing it from another script), it creates a Tk instance and calls that code. Here's a really simple example.
tkmodule.py:
from tkinter import *
def say_hi():
print("Hello, world!")
def create_interface(window):
hi = Button(window, text='Hello', command=say_hi)
hi.pack()
if __name__ == '__main__':
root = Tk()
create_interface(root)
root.mainloop()
tkscript.py:
from tkinter import *
import tkmodule
i = 0
def count():
global i
i += 1
print(i)
def create_interface(window):
countbtn = Button(window, text='Count', command=count)
countbtn.pack()
root = Tk()
create_interface(root)
window = Toplevel(root)
tkmodule.create_interface(window)
root.mainloop()
Now, when you run tkscript.py, it owns one Tk instance, and passes it to its own create_frame and to tkmodule.create_frame. But if you just run tkmodule.py, it owns a Tk instance, which it passes to its own create_frame. Either way, there's exactly one Tk instance, and one main loop, and everyone gets to use it.
Notice that if you want two top-level windows, you have to explicitly create a Toplevel somewhere. (And you don't want to always create one in tkmodule.py, or when you run the module itself, it'll create a new window and leave the default window sitting around empty.)
Of course an even simpler way to do this is to put all of your GUI stuff into modules that never create their own Tk instance, and write scripts that import the appropriate modules and drive them.
How to create multi-lines in an entry widget in tkinter and use those inputs to create something?
For example, I want a textbox widget to come up and ask the user:
How many squares do you want? (ex: 4x4, 5x5)
What color do you want them?
And with the users input, I would like to create that many x-amount of squares in that specific height/width and specify the colors etc.
I am totally new to tkinter and I'm not really sure how to approach this.
I tried using this, but i'm not really sure how to add more lines and to use the values inputted.
import tkinter
from tkinter import *
class Squares:
root = Tk()
root.title('Random')
x = Label(text='How many squares? (ex: 4x4, 5x3)').pack(side=TOP,padx=10,pady=10)
Entry(root, width=10).pack(side=TOP,padx=10,pady=10)
Button(root, text='OK').pack(side= LEFT)
Button(root, text='CLOSE').pack(side= RIGHT)
You have a number of problems here.
I'm not sure what the Squares class is supposed to be doing, but it's basically not doing anything. You have a bunch of code that runs when you define the class, creating a few variables (which will end up as class attributes, shared by all instances of the class), and… that's it. Rather than try to figure out what you're intending here, I'm just going to scrap the class and make it all module-level code.
You never call root.mainloop(), so your program will just define a GUI and then never run it.
You don't bind your buttons to anything, so there's no way they can have any effect. You need to create some kind of function that does something, then pass it as the command argument, or .bind it later.
You don't store references for any of your controls, so there's no way to access them later. If you want to get the value out of the entry, you need some way to refer to it. (The exception is your x variable, but that's going to be None, because you're setting it to the result of calling pack on the Label, not the Label itself.)
Once you've done that, you just need to parse the value, which is pretty easy.
Putting it all together:
import tkinter
from tkinter import *
root = Tk()
root.title('Random')
Label(text='How many squares? (ex: 4x4, 5x3)').pack(side=TOP,padx=10,pady=10)
entry = Entry(root, width=10)
entry.pack(side=TOP,padx=10,pady=10)
def onok():
x, y = entry.get().split('x')
for row in range(int(y)):
for col in range(int(x)):
print((col, row))
Button(root, text='OK', command=onok).pack(side=LEFT)
Button(root, text='CLOSE').pack(side= RIGHT)
root.mainloop()
You just have to change that print to do something useful, like creating the squares.
If you don't need an outline for the text box, create_text would be the easiest thing, even though it doesn't have a wrap text feature(at least, in python 3 you can do this):
from tkinter import *
tk = Tk()
canvas = Canvas(tk, 1000, 1000)
canvas.pack()
canvas.create_text(200, 200, text="Example Text")
Try it!