When I run my Tkinter code the results end up giving me two Tkinter windows. One has the widgets/items my code says but one is a completely blank window.
file 1-
from tkinter import*
class Screen:
def __init__(self, items):
self.Tk = Tk()
self.items = items
self.Tk.mainloop()
file 2-
from tkinter import*
from Screen import*
class Module1:
def __init__(self):
Test_button = Button(text="Test")
Test_button.pack()
items = Module1()
Mainscreen = Screen(items)
I just want to make a modular Tkinter screen so my code is not that messy 😅
-Just want one/common Tkinter window
I think the trouble is you're going about things backwards a bit. You'll want to import your modules into the Python file containting your "root" window class (aka Screen), rather than the other way around.
The typical way to handle a "root" window class in tkinter usually looks like this, where the class inherits directly from Tk()
# this is the first file...
import tkinter as tk # avoid star imports!
from file2 import Module1 # import your widget classes here
# note that depending on your project structure you may need to do
# 'from .file2 import Module1' instead (this is called a 'relative import')
class Screen(tk.Tk): # the 'Screen' class inherits from 'tk.Tk'
def __init__(self):
super().__init__() # initialize tkinter.Tk()
# instance your module widgets by calling the class like so
# (you'll need to update Module1 as shown below here)
self.mod1 = Module1(self) # 'self' refers to 'Screen', the main window
You'll need to update the Module1 class so it can be bound to your parent Screen
# this is the second file...
import tkinter as tk # again, star imports bad...
class Module1:
def __init__(self, parent): # add the parent parameter
# whenever you instance your 'Module1' class, it will expect a 'parent';
# above, we used self (i.e. 'Screen') - this is where your 'button' is going!
self.parent = parent
test_button = tk.Button(self.parent, text="Test")
test_button.pack()
And you'll run the mainloop() method on an instance of your Screen class at the end of your main Python file like so
# this is the first file again...
if __name__ == '__main__': # if this script is being run at the top level...
app = Screen() # get an instance of your Screen class
app.mainloop() # run the GUI loop
Related
I am wondering if there is a way, in tkinter or using any other python module, to make it so you keep a label or any other element in every window made by just using something like a function that makes the label within the window? I've tried this:
#Modules
import tkinter as tkin
#The initializer window
class Test:
def __init__(self):
#Initializes the main window
self.start = tkin.Tk()
self.start.title('Test')
self.label_thing()
#Makes a label
def label_thing(self):
self.label1 = tkin.Label(text='Not Button')
self.label1.pack()
I don't know if I made any errors or if this isn't a thing you can do but I'd also like to refrain from having the label localized to this window and having to remake the code for every window.
Let us assume you have a button that creates windows, you would pass this window as an argument to the function that creates the label, so like:
import tkinter as tk # Fixed weird import naming
class Test:
def __init__(self):
self.start = tk.Tk()
self.start.title('Test')
self.label_thing(self.start) # Label on the main window
tk.Button(self.start,text='Click me',command=self.create_win).pack()
self.start.mainloop()
def label_thing(self, master):
self.label1 = tk.Label(master, text='Not Button')
self.label1.pack()
def create_win(self):
new = tk.Toplevel()
self.label_thing(new) # Label on the new window
if __name__ == '__main__':
Test()
As you can see, as long as you press the button, new windows are created, and all those windows have the label on them, dynamically.
I have a main tkinter window that can take up to a few seconds to load properly. Because of this, I wish to have a splash screen that shows until the init method of the main class has finished, and the main tkinter application can be shown. How can this be achieved?
Splash screen code:
from Tkinter import *
from PIL import Image, ImageTk
import ttk
class DemoSplashScreen:
def __init__(self, parent):
self.parent = parent
self.aturSplash()
self.aturWindow()
def aturSplash(self):
self.gambar = Image.open('../output5.png')
self.imgSplash = ImageTk.PhotoImage(self.gambar)
def aturWindow(self):
lebar, tinggi = self.gambar.size
setengahLebar = (self.parent.winfo_screenwidth()-lebar)//2
setengahTinggi = (self.parent.winfo_screenheight()-tinggi)//2
self.parent.geometry("%ix%i+%i+%i" %(lebar, tinggi, setengahLebar,setengahTinggi))
Label(self.parent, image=self.imgSplash).pack()
if __name__ == '__main__':
root = Tk()
root.overrideredirect(True)
progressbar = ttk.Progressbar(orient=HORIZONTAL, length=10000, mode='determinate')
progressbar.pack(side="bottom")
app = DemoSplashScreen(root)
progressbar.start()
root.after(6010, root.destroy)
root.mainloop()
Main tkinter window minimum working example:
import tkinter as tk
root = tk.Tk()
class Controller(tk.Frame):
def __init__(self, parent):
'''Initialises basic variables and GUI elements.'''
frame = tk.Frame.__init__(self, parent,relief=tk.GROOVE,width=100,height=100,bd=1)
control = Controller(root)
control.pack()
root.mainloop()
EDIT: I can use the main window until it has finished loading using the .withdraw() and .deiconify() methods. However my problem is that I cannot find a way to have the splash screen running in the period between these two method calls.
a simple example for python3:
#!python3
import tkinter as tk
import time
class Splash(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.title("Splash")
## required to make window show before the program gets to the mainloop
self.update()
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.withdraw()
splash = Splash(self)
## setup stuff goes here
self.title("Main Window")
## simulate a delay while loading
time.sleep(6)
## finished loading so destroy splash
splash.destroy()
## show window again
self.deiconify()
if __name__ == "__main__":
app = App()
app.mainloop()
one of the reasons things like this are difficult in tkinter is that windows are only updated when the program isn't running particular functions and so reaches the mainloop. for simple things like this you can use the update or update_idletasks commands to make it show/update, however if the delay is too long then on windows the window can become "unresponsive"
one way around this is to put multiple update or update_idletasks command throughout your loading routine, or alternatively use threading.
however if you use threading i would suggest that instead of putting the splash into its own thread (probably easier to implement) you would be better served putting the loading tasks into its own thread, keeping worker threads and GUI threads separate, as this tends to give a smoother user experience.
I have a question. I thought that a class can be based on an object or previously define class. When I change it into class Application(object): it doesn't work. Can you tell me why it didn't work and why did the code below works or why did class Application(Frame) works? Frame is not a previously define object and not object. Sorry for my English. Here is my code:
# Lazy Buttons
# Demonstrates using a class with tkinter
from tkinter import *
class Application(Frame): #
""" A GUI application with three buttons. """
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
""" Create three buttons that do nothing. """
# create the first Button
self.bttn1 = Button(self, text= "I do nothing.")
self.bttn1.grid()
# create second button
self.bttn2 = Button(self)
self.bttn2.grid()
self.bttn2.configure(text="Me too!")
# create third button
self.bttn3 = Button(self)
self.bttn3.grid()
self.bttn3["text"] = "Same here!"
# main
root= Tk()
root.title("Lazy Buttons")
root.geometry("200x85")
app = Application(root)
root.mainloop()
Frame is a previously defined class. It's part of tkinter, which you imported on your first line.
Your Application class extends Frame, which means that it gets methods from Frame and can do everything that a Tk Frame can do, like show widgets. If you don't extend Frame, and only extend object instead, this will not work.
It might be clearer to replace...
from tkinter import *
with...
import tkinter as tk
and fix your references to Tk's classes (they would become tk.Button, tk.Frame and tk.Tk).
I've got a tkinter window that have 3 features: background color,foreground color, and a text label. These features is in a text config file (properties.conf) in my home folder. I want to update window features when the config file changed. I watch the changes in config file with pyinotify and I want to update window when it changes. This is the code:
#!/usr/bin/python
import threading
from Tkinter import *
import os
import ConfigParser
import pyinotify
class WatchFile(threading.Thread):
def run(self):
def onChange(ev):
Gui().updateGUI()
print 2
wm = pyinotify.WatchManager()
wm.add_watch('/home/mnrl/window_configs', pyinotify.IN_CLOSE_WRITE, onChange)
notifier = pyinotify.Notifier(wm)
notifier.loop()
class ConfigParse():
def __init__(self):
self.confDir = os.path.join(os.getenv('HOME'), 'window_configs/')
self.confFile = os.path.join(self.confDir + "properties.conf")
self.config = ConfigParser.ConfigParser()
if os.path.isfile(self.confFile):
self.config.read(self.confFile)
else:
if not os.path.exists(self.confDir):
os.makedirs(self.confDir)
self.config.add_section('bolum1')
self.config.set('section1', 'setting1', 'green')
self.config.set('section1', 'setting2', 'red')
self.config.set('section1', 'setting3', 'sample text')
with open(self.confFile, 'wb') as self.confFile:
self.config.write(self.confFile)
class Gui(object):
def __init__(self):
self.root = Tk()
self.lbl = Label(self.root, text=ConfigParse().config.get('section1', 'setting3'), fg=ConfigParse().config.get('section1', 'setting1'), bg=ConfigParse().config.get('section1', 'setting2'))
self.lbl.pack()
def updateGUI(self):
self.lbl["text"] = ConfigParse().config.get('bolum1', 'ayar3')
self.lbl["fg"] = ConfigParse().config.get('bolum1', 'ayar1')
self.lbl["bg"] = ConfigParse().config.get('bolum1', 'ayar2')
self.root.update()
WatchFile().start()
Gui().root.mainloop()
But whenever properties.conf file changes a new window more appears near the old tkinter window. So tkinter window not updates, new windows open. How can I correct it?
The problem is that in WatchFile.run() you're doing this:
def onChange(ev):
Gui().updateGUI()
This does something different than what you're expecting. It creates a new GUI instance, and then immediately calls the updateGUI() method on it. Hence the two windows.
What you need to do instead, is something along the lines of:
#!/usr/bin/env python
gui = None
class WatchFile(threading.Thread):
def run(self):
def onChange(ev):
gui.updateGUI()
[...]
WatchFile().start()
gui = Gui()
gui.root.mainloop()
Here, a variable gui is created, and has an instance of your GUI class assigned to it. Later, the updateGUI() method is called on the same instance.
This problem is more or less repeated with your usage of your ConfigParser class, for example:
self.lbl["text"] = ConfigParse().config.get('bolum1', 'ayar3')
In this case, it 'works' because your ConfigParse() class can be executed twice without side-effects (such as opening windows), but it's not very efficient. You're reading the same file multiple times.
What would be better, is to just use a function (a class with only a __init__ defined is effectively the same), run this once, and return a dict.
I am currently stuck on a problem with Python and Tkinter.
I want to create a simple application with its UI made on Tkinter. To do so, I created a class to define my application and I want to create my GUI layout in a separate class function.
However, when I call it, it has no effect on my Tk window (in this particular example, the title is not modified)
Here is the code
from Tkinter import *
fen =Tk()
class test_Tk_class:
def __init__(self):
self.make_title
def make_title(self):
fen.title("Test")
a = test_Tk_class()
fen.mainloop()
Thanks for any help !
You're missing () after self.make_title:
from Tkinter import *
fen =Tk()
class test_Tk_class:
def __init__(self):
self.make_title() # <------------
def make_title(self):
fen.title("Test")
a = test_Tk_class()
fen.mainloop()