So for some reason the scale function in tkinter doesn't want to output the number on the scale. All i receive is either 0.0 or nothing. It seems to be to do with the GUI and calling functions through the GUI. Written and run in python 3.4.
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
iterations=30
def settings():
global itervar, iterscale
sGui = Tk()
itervar = DoubleVar()
iterscale = Scale(sGui, orient="horizontal", from_=1, to=1000, variable=itervar)
iterscale.pack()
iterbutton = Button(sGui, text="Save Settings", command=saveSettings)
iterbutton.pack()
sGui.mainloop()
def saveSettings():
global iterations
iterations = itervar.get()
print(iterations)
def doNothing():
pass
def main():
global root, version
root= Tk()
menu = Menu(root)
root.config(menu=menu)
fileMenu = Menu(menu)
menu.add_cascade(label="File", menu=fileMenu)
fileMenu.add_command(label="Quit", command=quit)
benchmarkMenu = Menu(menu)
menu.add_cascade(label="Benchmark", menu=benchmarkMenu)
benchmarkMenu.add_command(label="Run [All]", command=doNothing)
benchmarkMenu.add_separator()
benchmarkMenu.add_command(label="Settings", command=settings)
root.mainloop()
#Main
main()
I have tried the functions settings and saveSettings on their own and they work fine, but when i call it through the GUI it doesn't seem to work.
Any ideas on the problem, my only solution would be have the settings function and saveSettings function in a different file and then run that file externally with os.startfile("etc...")
Minimal fix: change this
itervar = DoubleVar()
to this:
itervar = DoubleVar(sGui)
Because you have two root applications (root and sGui are both instances of Tk) the implied parent widget for itervar is the first one created, being root so tkinter gets confused when you specify it as the variable for a completely different application.
But I would highly recommend using a Toplevel instance to keep the windows a part of the same program:
sGui = Toplevel(root)
...
#sGui.mainloop() #no longer need this
although if you want to be able to run the setting window without the main one you might consider making all your visible windows Toplevels and make the actual root hidden:
# I'm not sure if you want to call it this
abs_root = Tk() # but you are already using root
abs_root.withdraw() #hide the window
Then make root = Toplevel(abs_root)
You coud phase out the variable all together by using .geting the scale directly:
iterations = iterscale.get()
Related
I have a main program window with a frame and a menu for invoking calculation modules.
from tkinter import Menu, Label, Tk, Frame, Scrollbar, Canvas, N, E, S
import runpy
import Bolt_connection
from importlib import reload
# Program window dimensions
root = Tk()
root.title("Name")
root.geometry("1260x700")
root.update()
MaxX = root.winfo_width()
MaxY = root.winfo_height()
canvas = Canvas(root, width=MaxX, height=MaxY, bg="white")
scroll_y = Scrollbar(root, orient="vertical", command=canvas.yview)
frame = Frame(canvas)
for i in range(50):
Label(frame).grid()
i += 1
canvas.create_window(0, 0, anchor='nw', window=frame)
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox('all'),
yscrollcommand=scroll_y.set)
canvas.grid()
scroll_y.grid(row=0, sticky=E + N + S)
main_menu = Menu()
# Submenu for Export to...
file_menu = Menu(tearoff=0)
help_menu = Menu(file_menu, tearoff=0)
help_menu_export_to = Menu(help_menu, tearoff=0)
help_menu_export_to.add_command(label="Excel")
help_menu_export_to.add_command(label="Word")
def start_bolt_connection():
import Bolt_connection
def start_key_connection():
import Key_connection
# Submenu for Part calculation selection
calculation_selection_menu = Menu(tearoff=0)
help_menu2 = Menu(calculation_selection_menu, tearoff=0)
help_menu2_part_calculation_selection = Menu(help_menu2, tearoff=0)
B2 = help_menu2_part_calculation_selection.add_command(label="Bolted (screw) connection", command=start_bolt_connection)
help_menu2_part_calculation_selection.add_command(label="Key connection", command=start_key_connection)
help_menu2_part_calculation_selection.add_command(label="Pinned connection")
# File menu
file_menu.add_command(label="New")
file_menu.add_command(label="Save")
file_menu.add_cascade(label="Export to...", menu=help_menu_export_to)
file_menu.add_command(label="Open")
file_menu.add_command(label="Exit")
# Main menu
main_menu.add_cascade(label="File", menu=file_menu)
main_menu.add_cascade(label="About")
main_menu.add_cascade(label="Help")
main_menu.add_cascade(label="Part calculation selection", menu=help_menu2_part_calculation_selection)
runpy.run_module('Bolt_connection')
root.config(menu=main_menu)
root.mainloop()
I want to see a window that looks like this when the program starts. Then, when you click on one of the menu buttons (for example, Key connection), the frame should update and display the graphical interface of the new module. After clicking the window should look like this.
The problem is that when the graphic part of the new module is displayed, the graphic part of the old module is not erased and they overlap each other. How to make the program delete the graphic part of the old module? The grid_remove() and reload() functions do not help.
An additional explanation of what I want to get when I run the program.
Program starts.
The main window appears with the calculation module already running, installed by default. In this case, "Bolt connection". The window should now look like this.
The user selects one of the calculation modules from the menu. In this case, "Key connection". The graphic part of the previous module is erased, and the new module is displayed. The window should now look like this.
UPD
For a better understanding of the problem, add a link to google drive where the project files are located.
https://drive.google.com/drive/folders/110OrRqlfzGmswg8cGHO3-C4VxfRj1sos?usp=sharing
In this folder there are two txt files with the code of the exported modules, so that you do not have to download the project to your computer and run it .
main.py
import tkinter as tk
import Mod1
import Mod2
root = tk.Tk()
m1 = Mod1.Model(root)
m2 = Mod2.Model(root)
m1.grid(column= 0, row=0)
m2.grid(column = 0 ,row=0)
def callback():
m1.lift()
b = tk.Button(text='click me', command=callback)
b.grid(column=0,row=1)
Mod1.py
from __main__ import tk
class Model(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.configure(bg="red", width=300, height=300)
Mod2.py
from __main__ import tk
class Model(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.configure(bg="blue", width=300, height=300)
Explaination
In your Modules you define your Model's as a subclass of tk.Frame, that gives you the ability to handle your Models as same as a tk.Frame.
I'm trying to use two dialogs to get manual input, and then work with that data.
All source I've found claim I should use the get() function, but I wrote a simple mini program yet, and I can't make the second dialog work.
I hope someone can tell me what I'm doing wrong. Here's a file:
from tkinter import *
from tkinter.filedialog import askdirectory
from tkinter import messagebox
def getpath():
def selectPath():
path_ = askdirectory()
path.set(path_)
root = Tk()
root.title('select path')
path = StringVar()
def close():
if(path.get()==""):
messagebox.showinfo("","nothing")
else:
root.withdraw()
root.quit()
Label(root,text="path:").grid(row=0,column=0)
Entry(root,textvariable = path).grid(row=0,column=1)
Button(root,text="select",command=selectPath).grid(row=0,column=2)
Button(root,text="enter",command=close).grid(row=0,column=3)
root.mainloop()
return path.get()
def getname():
def get_need_name():
name = need_name.get()
print('hereherehere'+name) #does not work
root = Tk()
root.title('select name')
need_name = StringVar()
Label(root,text="name:").grid(row=0,column=0)
entry = Entry(root,bd=10,textvariable=need_name)
entry.grid(row=0,column=1)
Button(root,text="enter", font=16, bg="silver", relief='groove', command=get_need_name).grid(row=0,column=2)
root.mainloop()
return name.get()
def main():
path = getpath()
print("mypath:"+path)
print('******************')
print('done!')
name = getname()
print("myname:"+name)
if __name__ == '__main__':
main()
This give me two dialogs I can type in, but only the first dialog works.
The reason is that you are creating multiple instances of Tk, and you don't destroy the instances when you are done with them. This causes two problems. First is a memory leak. Each time you call one of these functions you create a new window and a new tcl interpreter.
The second problem is that the first root window becomes the default window when a root isn't specified. When you create a StringVar in the second function, because you didn't specify which root window it belongs to it will be assigned to the first root window. When you use it as the target of textvariable in a second instance of Tk, tkinter thinks the variable doesn't exist so it creates a new one for the second window. However, your reference is still to the one created in the first root window and is never updated by user input in the second window.
Confusing? Yes, which is why you typically shouldn't be creating more than one instance of Tk.
To make your code work with as few changes as possible and to remove the memory leak caused by not destroying the windows, you can change the last couple of lines in your method to look like the following. This destroys the root window when you are done with it, removing the memory leak and the side effect of having more than one root window.
root = Tk()
...
root.mainloop()
value = path.get()
root.destroy()
return value
The second dialog should look similar:
root = Tk()
...
root.mainloop()
value = name.get()
root.destroy()
return value
This retrieves the value after mainloop exits but before the underlying tcl interpreter is deleted, and then destroys the window and its tcl interpreter.
The next time you create an instance of Tk, that instance will become the new default root, and any new instance of StringVar will go to that root.
Another solution would be to specify the master for the StringVar instance, but that leaves the memory leak in place so it's only half of a solution.
Arguably a better solution is to create a single root window, and either reuse it or create instances of Toplevel rather than Tk. Effbot has some decent documentation on how to create a modal window with wait_window.
After some testing, and googling the root.quit() is the problem
here is a working example for you to look at and mess with.
from tkinter import *
from tkinter.filedialog import askdirectory
from tkinter import messagebox
root = Tk()
path = StringVar()
def select_path():
#uses the return value to set no need to create an additional variable
path.set(askdirectory())
def close():
if path.get() == "":
messagebox.showinfo("","Please select path")
else:
get_name_frame.tkraise()
def get_name():
print("hereherehere", name.get())
get_path_frame = Frame(root)
get_path_frame.grid(row=0, column=0, sticky="nsew")
Label(get_path_frame,text="path:").grid(row=0,column=0)
Entry(get_path_frame,textvariable = path).grid(row=0,column=1)
Button(get_path_frame,text="select",command=select_path).grid(row=0,column=2)
Button(get_path_frame,text="enter",command=close).grid(row=0,column=3)
get_name_frame = Frame(root)
get_name_frame.grid(row=0, column=0,sticky="nsew")
Label(get_name_frame, text="name: ").grid(row=0, column=0)
name = StringVar()
entry = Entry(get_name_frame, bd=10, textvariable = name)
entry.grid(row=0, column=1)
Button(get_name_frame,text="enter", font=16, bg="silver", relief='groove', command=get_name).grid(row=0,column=2)
get_path_frame.tkraise()
root.mainloop()
I created a button that works perfectly (not entire code here) but I want that once you press the 'Save' button, the window disappear. Someone knows how to do it?
root2 = tk.Tk()
root2.geometry('200x100')
save_button = tk.Button(root2)
save_button.configure(text='Save', command=lambda: ask_parameter(ents1))
save_button.pack()
root2.mainloop()
Based on the extremely limited snippet of code in your question: I would suggest it doing by defining a function to call that does something like this:
import tkinter as tk
def ask_and_close(root, ents):
ask_parameter(ents)
root.destroy()
ents1 = "something"
root2 = tk.Tk()
root2.geometry('200x100')
save_button = tk.Button(root2)
save_button.configure(text='Save', command=lambda: ask_and_close(root2, ents1))
save_button.pack()
root2.mainloop()
Note: If you're creating multiple windows, I wouild suggest using tk.Toplevel() instead of calling tk.TK() more than once.
Just use the master.quit() method!
Example Code:
from tkinter import *
class Test:
def __init__(self):
self.master = Tk()
self.button = Button(self.master, text="Push me!", command=self.closeScreen)
self.button.pack()
def closeScreen(self):
# In your case, you need "root2.quit"
self.master.quit()
test = Test()
mainloop()
I would suggest using destroy() method as used here https://docs.python.org/3.8/library/tkinter.html#a-simple-hello-world-program.
One of the easy ways to invoke the destroy method in your code is this;
def ask_parameter_and_destroy(ents1):
ask_parameter(ents1)
root2.destroy()
root2 = tk.Tk()
root2.geometry('200x100')
save_button = tk.Button(root2)
save_button.configure(text='Save', command=lambda: ask_parameter_and_destroy(ents1))
save_button.pack()
root2.mainloop()
You can read about differences between destroy() and previously proposed quit() methods on the following page: What is the difference between root.destroy() and root.quit()?.
If your goal is to create a dialog for saving a file, you might be interested in tkinter.filedialog library, which has ready made dialog boxes for handling file saving.
I'm a new member to Stack Overflow.
I found this thread, but was not allowed to comment or ask questions on it, so I thought I'd just reference it here: How can I make a in interactive list in Python's Tkinter complete with buttons that can edit those listings?
from tkinter import *
import os
import easygui as eg
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
# character box
Label(frame, text = "Characters Editor").grid(row = 0, column = 0, rowspan = 1, columnspan = 2)
charbox = Listbox(frame)
for chars in []:
charbox.insert(END, chars)
charbox.grid(row = 1, column = 0, rowspan = 5)
charadd = Button(frame, text = " Add ", command = self.addchar).grid(row = 1, column = 1)
charremove = Button(frame, text = "Remove", command = self.removechar).grid(row = 2, column = 1)
charedit = Button(frame, text = " Edit ", command = self.editchar).grid(row = 3, column = 1)
def addchar(self):
print("not implemented yet")
def removechar(self):
print("not implemented yet")
def editchar(self):
print("not implemented yet")
root = Tk()
root.wm_title("IA Development Kit")
app = App(root)
root.mainloop()
Could somebody explain to me why the very last line is root.mainloop()?
Being a novice with Python, and coming from a background that's procedural-oriented with no object-orient experience, I would have thought it would have been app.mainloop().
In fact app = App(root) , app is never used again in the rest of the code! I'm having trouble understanding why root.mainloop() still works.
I'm not sure if you'll find this answer satisfying, but you call root.mainloop() because root is the object that has the mainloop method. In the code you've given, App has no mainloop function.
In simpler terms, this is just how tkinter works -- you always end your script by calling the mainloop method of the root window. When that method returns, your program will exit.
Let's start at the beginning. The simplest, non-OO Tkinter program is going to look like the following example. Note that this is a python 2.x example, and I do not use a global import since I think global imports are bad.
import Tkinter as tk
root = tk.Tk()
<your widgets go here>
root.mainloop()
Many people find that a pure procedural style is not an effective way to write code, so they might choose to write this in an object-oriented style. It's natural to think of "the app" as a singleton object. There are many ways to do this -- the one in your question is, unfortunately, not one of the clearer ways to do it.
A slightly better way, IMO, would be to structure the code like this:
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
<your widgets go here>
app = App()
app.mainloop()
In this case, mainloop is still being called, though now it's a method of App since App inherits from Tk. It is conceptually the same as root.mainloop() since in this case, app is the root window even though it goes by a different name.
So, in both cases, mainloop is a method that belongs to the root window. And in both cases, it must be called for the GUI to function properly.
There is a third variation which is what the code you picked is using. And with this variation, there are several ways to implement it. The variation is your question uses a class to define the GUI, but does not inherit from Tk. This is perfectly fine, but you still must call mainloop at some point. Since you don't create (or inherit) a mainloop function in your class, you must call the one associated with the root window. The variations I speak of are how and where the instance of App is added to the root window, and how mainloop is ultimately called.
Personally I prefer that App inherits from Frame, and that you pack the app outside the definition of the app. The template I use looks like this:
class App(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
<your widgets go here>
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
app.pack(fill="both", expand=True)
root.mainloop()
In this final example, app and root are two completely different objects. app represents a frame that exists inside the root window. Frames are commonly used this way, as a container for groups of other widgets.
So, in all cases, mainloop must be called. where it is called, and how, depends a bit on your coding style. Some people prefer to inherit from the root window, some don't. In either case, you must call the mainloop function of the root window.
I tested both like you see:
One is written with "app." + ".pack()" and one calls "mainframe." + ".grid()"
#-*- coding: utf-8 -*-
#THIS IS THE "MAINFRAME." - PART
from Tkinter import *
import ttk
def show():
p = password.get() #get password from entry
print(p)
root = Tk()
root.title("Ingos first program")
mainframe = ttk.Frame(root, padding="30 30 60 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
password = StringVar() #Password variable
passEntry = Entry(mainframe, textvariable=password, show='*').grid(column=3, row=3, sticky=S)
submit = Button(mainframe, text='Show Console',command=show).grid(column=3, row=4, sticky=S)
root.mainloop()
def show():
p = password.get() #get password from entry
print(p)
#THIS IS THE "APP."-PART. BOTH WORKS FINE.
app = Tk()
app.title("Ingos first program")
password = StringVar() #Password variable
passEntry = Entry(app, textvariable=password, show='#').pack()
submit = Button(app, text='Show Console',command=show).pack()
app.mainloop()
This instance works fine with python 2.7. In that test even app. can handle "mainloop()"
That script opens 2 windows, one after another (if you close the first one) and the first programm is formatted, didn't tryed to write the colum=3... stuff in the pack() clamps.
I still started the Tkinter so don't fight me, just trying.. Hope I could help to answer your question.
All the best, Ingo
The App object is just your app code, and the reason you call App(root) is to make an instance with your class, which then has access to your root window.
It receives this reference in the __init__ method:
def __init__(self, master):
# master refers to the root window now
...
You can see the entire definition of the App object (given by the block beginning with class App:), and it doesn't even have a mainloop method, so to start the main Tkinter loop, you have to call it on the root window.
In the example in the Python2 documentation, they do call it as you suspected should be done, but note that their example class subclasses the Tk object Frame. In your example code, App is an old-style class that doesn't inherit anything.
I am using Tkinter to build two windows. One the main one, pressing a button leads to the creation of the second window.
This second window does not get focus immediately when it created. That I am able to fix by calling .focus_force(). However, when I call the askdirectory() function from tkFileDialog, the focus changes back to the first window.
How can I prevent that focus switch from happening, without simply calling focus_force() all over the place?
To replicate problem:
from Tkinter import *
from tkFileDialog import *
class app:
def __init__(self, master):
Button(master, command = make_new).grid()
def make_new(self):
root = Tk()
new = new_win(root)
root.mainloop() #here the focus is on the first window
class new_win:
def __init__(self, master):
f = askdirectory() #even after placing focus on second window,
#focus goes back to first window here
I am using Python 2.7.3. Thanks!
the little-documented wm_attributes method might help:
from Tkinter import *
import tkFileDialog
root = Tk()
top = Toplevel()
top.wm_attributes('-topmost', 1)
top.withdraw()
top.protocol('WM_DELETE_WINDOW', top.withdraw)
def do_dialog():
oldFoc = top.focus_get()
print tkFileDialog.askdirectory()
if oldFoc: oldFoc.focus_set()
b0 = Button(top, text='choose dir', command=do_dialog)
b0.pack(padx=100, pady=100)
def popup():
top.deiconify()
b0.focus_set()
b1 = Button(root, text='popup', command=popup)
b1.pack(padx=100, pady=100)
root.mainloop()