Tkinter text editor thats always on top - python

So i just want to make this window a text widget thats always on top. Im trying to add this widget but it keeps creating another window...
import tkinter as tk
from tkinter import ttk
class App(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.pack()
hello="hello"
self.tb = tk.Text(self)
self.tb.pack(expand=1, fill = tk.BOTH)
self.tb.insert(tk.END, hello)
topLevelWindow = tk.Toplevel(self)
# Make topLevelWindow remain on top until destroyed, or attribute changes.
topLevelWindow.attributes('-topmost', 'true')
if __name__ == "__main__":
root = tk.Tk()
main = App(root)
root.mainloop()

If you want your text widget to be active you need to call this instead:
self.tb.focus_set()
What you're doing is instead create a Toplevel widget, which is a window that stays topmost, as in above all windows in desktop, you should remove:
topLevelWindow = tk.Toplevel(self)
# Make topLevelWindow remain on top until destroyed, or attribute changes.
topLevelWindow.attributes('-topmost', 'true')
If you also want that your entire window you can do that for your root instead in your 'main' according to this answer:
root.call('wm', 'attributes', '.', '-topmost', '1')
Finally to have:
import tkinter as tk
from tkinter import ttk
class App(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.pack()
hello="hello"
self.tb = tk.Text(self)
self.tb.pack(expand=1, fill = tk.BOTH)
self.tb.insert(tk.END, hello)
self.tb.focus_set()
if __name__ == "__main__":
root = tk.Tk()
root.call('wm', 'attributes', '.', '-topmost', '1')
main = App(root)
root.mainloop()
Also if you want to make other widgets unfocusable:
widget.config(takefocus=False)

Related

Menubar does not appear in tkinter when configured

I am creating an tkinter app. For now I just want to get a very basic menubar to work, with a file section, and an exit button in the sub menu. Here is my object oriented code, which may be where I am going wrong:
import tkinter as tk
class MainApplication(tk.Frame):
def __init_(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.menubar = tk.Menu(self)
self.filemenu = tk.Menu(self.menubar, tearoff=0)
self.filemenu.add_command(label="Exit", command=self.quit)
self.menubar.add_cascade(label="File", menu=self.filemenu)
self.config(menu=self.menubar)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
However, this only creates a blank tkinter window. This usually works for me when I use procedural programming so I think I am doing something wrong with OOP. I am trying to say self.config() as root.config(), but this does not work.
2 big issues there. The first is that you misspelled __init__, so none of your custom code is being run. The second is that you need to apply the menu to the root window, aka self.master (default name) or self.parent (your name). Try like this:
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.menubar = tk.Menu(self)
self.filemenu = tk.Menu(self.menubar, tearoff=0)
self.filemenu.add_command(label="Exit", command=self.quit)
self.menubar.add_cascade(label="File", menu=self.filemenu)
self.master.config(menu=self.menubar)
if __name__ == "__main__":
root = tk.Tk()
root.geometry('200x200') # remove once you've added window content
win = MainApplication(root)
win.pack(side="top", fill="both", expand=True)
root.mainloop()
I also moved you to a python3 inheritance style, and defined a size so that you actually see something.

Changing tab widgets from another tab in Tkinter

I am creating a GUI using tkinter based on the structure described here. I have some tabs that look identical but with different variables. So I decided to define a class for tabs and add them to the main window. I am going to configure some widgets in one tab from another tab. In line 11, a function is defined that when a button in tab_2 is clicked, tab_1's button background color changes to green. Whereas its working, I have two question:
Is it possible not to define channel_1 as an attribute of main_window? I think there must be better way to do so, specifically, if the GUI is going to be used as module (then main_window will not be defined).
Is it possible to know which tab is open, so when button in each tab is clicked, configurations in the other one changes only?
import tkinter as tk
from tkinter import ttk
class Channel(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.btn = tk.Button(self.parent, text = 'click me', command = self.change_green)
self.btn.pack()
def change_green(self):
main_window.channel_1.btn.config(bg = 'green') # line 11
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.tab_control = ttk.Notebook(self.parent)
self.tab_1 = ttk.Frame(self.tab_control)
self.tab_2 = ttk.Frame(self.tab_control)
self.tab_control.add(self.tab_1, text = 'tab 1')
self.tab_control.add(self.tab_2, text = 'tab 2')
self.tab_control.pack(fill = 'both', expand = 1)
self.channel_1 = Channel(self.tab_1)
self.channel_2 = Channel(self.tab_2)
if __name__ == "__main__":
root = tk.Tk()
main_window = MainApplication(root) # <<<< here defined main_window
main_window.pack(side="top", fill="both", expand=True)
root.mainloop()
I would create class MyTab and keep its widgets in this class, not in channel. It can also keep access to other tab(s) to button in one tab can change color in other tab.
Using tab's parent (self.master) I can get active tab, list of all tabs and activate other tab.
import tkinter as tk
from tkinter import ttk
class MyTab(tk.Frame):
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
#self.master = master # super() already set it
self.btn = tk.Button(self, text='click me', command=self.change_green)
self.btn.pack()
self.other_tab = None # default value at start
def change_green(self):
if self.other_tab:
# change color in other tab
self.other_tab.btn.config(bg = 'green')
# get active tab ID
print('active tab ID:', self.master.select())
# get button in active tab
active_tab = root.nametowidget(self.master.select())
print('active tab - btn text:', active_tab.btn['text'])
# get all tabs
print('all tabs:', self.master.children.items())
# set other tab as active
self.master.select(self.other_tab)
class MainApplication(tk.Frame):
def __init__(self, master, *args, **kwargs):
super().__init__(master, *args, **kwargs)
#self.master = master # super() already set it
self.tab_control = ttk.Notebook(self.master)
self.tab_1 = MyTab(self.tab_control)
self.tab_2 = MyTab(self.tab_control)
self.tab_1.other_tab = self.tab_2
self.tab_2.other_tab = self.tab_1
self.tab_control.add(self.tab_1, text = 'tab 1')
self.tab_control.add(self.tab_2, text = 'tab 2')
self.tab_control.pack(fill = 'both', expand = 1)
if __name__ == "__main__":
root = tk.Tk()
main_window = MainApplication(root)
main_window.pack(side="top", fill="both", expand=True)
root.mainloop()

issues with closing a window in tkinter

To keep this as short as possible - In my program I start with Page1 and when I press a button I want to open Page2 and close Page1, I have managed to open Page2 but I cant close Page1, I have tried using .destroy() but it closes everything not just the page. I looked around some questions here on SO but couldn't find much in the same layout as my code so I wasnt sure how to apply it to mine. This is my first tkinter project so I am still getting to grips with it.
My code is;
class Page1:
def __init__(self,master):
self.master = master
#lots of labels and buttons
self.BTNNextPage = ttk.Button(master, text = "Proceed",
command = self.NextPage)
self.BTNNextPage.place(x=450, y=420)
def NextPage(self):
self.newWindow = tk.Toplevel(self.master)
self.app = Page2(self.newWindow)
self.master.destroy()
class Page2():
def __init__(self,master):
self.master = master
#tried Page1.destroy() here but Page1 has no attibute destroy
#more labels and buttons
def main():
widthpixels=690
heightpixels=500
root = tk.Tk()
root.resizable(width=False, height=False)
root.configure(background='black')
root.iconbitmap("Image")
root.wm_title("Title")
root.geometry('{}x{}'.format(widthpixels, heightpixels))
app = Page1(root)
root.mainloop()
if __name__ == "__main__":
main()
If you destroy root, it destroys all the widgets contained, including Page2. To destroy only page 1, one possibility is to make the page classes inherit from tk.Frame, so that they have a destroy method:
import tkinter as tk
from tkinter import ttk
class Page1(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.pack(fill='both', expand=True) # display page 1
#lots of labels and buttons:
tk.Label(self, text='Page 1').place(relx=0.5, rely=0.5)
self.BTNNextPage = ttk.Button(self, text="Proceed", command=self.NextPage)
self.BTNNextPage.place(x=450, y=420)
def NextPage(self):
self.app = Page2(self.master) # create page 2
self.destroy() # remove page 1
class Page2(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.pack(fill='both', expand=True) # display page 2
# create widgets on page 2
tk.Label(self, text='Page 2').pack()
tk.Button(self, text='Quit', command=self.master.destroy).pack(side='bottom')
def main():
widthpixels=690
heightpixels=500
root = tk.Tk()
root.resizable(width=False, height=False)
root.configure(background='black')
root.wm_title("Title")
root.geometry('{}x{}'.format(widthpixels, heightpixels))
app = Page1(root)
root.mainloop()
if __name__ == "__main__":
main()

Create a 'close/exit' option for a child frame that is created using tkinter in python

I have created a script using tkinter, that brings up a child frame when you press a button. The frame occupies the full screen size on my mac laptop. Now I need to create an option to exit/ or close this new frame. What is the best option to do that?
-Thanks
from Tkinter import *
import tkFont
class App(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.apple = Button(self,
text="Apple", command=self.write_apple)
self.apple.pack(side=LEFT)
def write_apple(self):
self.customFont = tkFont.Font(family="Helvetica", size=80)
t = Toplevel(self)
t.overrideredirect(True)
t.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
l = Label(t, text="This is a green apple.",font=self.customFont)
l.pack(side="top", fill="both", expand=True)
if __name__ == "__main__":
root = Tk()
main = App(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
This solution handles the case that multiple instances of Toplevel are created:
from Tkinter import *
import tkFont
class App(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.apple = Button(self,
text="Apple", command=self.write_apple)
self.apple.pack(side=LEFT)
self.top_dict = dict()
def destroy_top(self, event):
# event.widget is the instance of Label that was clicked
# Get the instance of Toplevel
top = self.top_dict[event.widget]
# Destroy the instance of Toplevel
top.destroy()
# Remove the instance of Toplevel from the list
del self.top_dict[event.widget]
def write_apple(self):
self.customFont = tkFont.Font(family="Helvetica", size=80)
# Create an instance of Toplevel
top = Toplevel(self)
top.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
label = Label(top, text="This is a green apple.",font=self.customFont)
label.pack(side="top", fill="both", expand=True)
# Bind the destroy_top method to the mouse-button-1 click
label.bind('<Button-1>', self.destroy_top)
# Save the instance of Toplevel using the label as the key
self.top_dict[label] = top
if __name__ == "__main__":
root = Tk()
main = App(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
NOTE: As #Bryan Oakley noted, removing the call to overrideredirect adds window decorations to the instance of Toplevel.

Can you combine two tkinter tk widgets?

I am creating an app which will have two Tk() widgets. Is it possible to combine them side into one larger widget side by side to make the app easier to use?
from tkinter import *
tk = Tk()
canvas = Canvas(tk,width=400, height=150)
canvas.pack()
tk2 = Tk()
canvas2 = Canvas(tk2,width=400, height=150)
canvas2.pack()
tk.mainloop(), tk2.mainloop()
When I do this to make the basic windows, I obviously get two seperate windows. Can that be combined into one?
I am a beginner and am using python 3.3
Not sure if it is what you are looking for, but you can create two Frames inside your main Tk.
import tkinter as tk
class SubWindow(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
x = tk.Text(self)
x.pack()
class MainWindow(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self)
self.win1 = SubWindow(self)
self.win1.pack(side="left")
self.win2 = SubWindow(self)
self.win2.pack(side="right")
if __name__ == "__main__":
main = MainWindow()
main.mainloop()
EDIT:
Here is the code to make Frames resize when window does:
import tkinter as tk
class SubWindow(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
x = tk.Text(self)
x.pack(expand=1, fill='both')
class MainWindow(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self)
self.win1 = SubWindow(self)
self.win1.pack(side="left", expand=1, fill=tk.BOTH)
self.win2 = SubWindow(self)
self.win2.pack(side="right", expand=1, fill=tk.BOTH)
if __name__ == "__main__":
main = MainWindow()
main.mainloop()
You should not have two instances of Tk in the same app. If you want multiple windows, you should create instances of Toplevel. If you want everything in one window you would create instances of Frame and arrange them however you want (side by side, top to bottom, in a grid, etc).
While it's possible to have multiple instances of Tk, there are side effects that can cause unexpected problems. Tk (upon which tkinter is built) was designed to have a single root widget.

Categories