Trying To Add TK Text Window to Example, Import tkinter confusion - python

I have been working with several tkinter tutorials including one with Text window and a very helpful tutorial but with out text window. See here: https://www.youtube.com/watch?v=oV68QJJUXTU
I have tried to add a Text window to this example but found the constant END was not defined and the Text window did not open or show up in the frame. I traced it down to a difference in the import. Using "from tkinter import *" the constant END was defined (it was 'end') but using the method of this tutorial, "import tkinter as tk" the constant END was not defined. I defined it to clear the error when I try to use Text window the window never opens (never shows up) in the example so I think either I have to rewrite to use the import * method or I need to understand how to over come the import as tk difference.
It seams that importing as tk is likely to be the more correct method rather than as * so that is the way I think I should be learning to do it.
Any suggestions out there?
This code works
from tkinter import *
.....
class set_window(Thread):
def __init__(self, labelText):
Thread.__init__(self)
self.labelText = labelText
self.labelText.set("Text Window Display")
self.T = Text(root, height=40, width=60, bd=10)
self.T.grid(row=1, column=0)
self.T.focus_set()
self.T.insert(END, "Just a text Widget\nin two lines\n")
But this did not:
import tkinter as tk
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Blast it!", font=XLARGE_FONT)
label.pack(pady=10, padx=10)
label_2 = tk.Label(self, text="Input Text Here", font=LARGE_FONT)
label_2.pack(pady=10, padx=10)
self.T = tk.Text(self, height=40, width=60, bd=10)
# print(type(END))
# input ("Press Enter")
self.T.insert(END, "Just a text Widget\nin two lines\n")
self.T.insert('end', "Just a text Widget\nin two lines\n")
self.T.focus_set()

If you want to access END which is available when you do from tkinter import *, you'd have to access it as tk.END when you do import tkinter as tk. Or, you can simply use 'end'. Another solution would be from tkinter.constants import END.

I found that I had to prefix the END constant as tk.END and that cleared on error.
I found I had to add a PACK statement after the Text window insert statement. The page code becomes:
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Blast it!", font=XLARGE_FONT)
label.pack(pady=10, padx=10)
label_2 = tk.Label(self, text="Input Text Here", font=LARGE_FONT)
label_2.pack(pady=10, padx=10)
self.T = tk.Text(self, height=40, width=60, bd=10)
self.T.insert(tk.END, "Just a text Widget\nin two lines\n")
self.T.focus_set()
self.T.pack()

use "end" instead of END
from tkinter import *
self.T.insert("end", "Just a text Widget\nin two lines\n")

Related

Why while the sound is playing, other commands are not executed? [duplicate]

When I was trying to play a "yay" sound effect after the user has passed the test, I will show the credits. However, considering that some users are impatient and they don't want to see it, I made a tkinter window to show the option of whether to see or not. But when the user clicks on "continue", the tk windows freeze (Freezed window) and after a while, it is normal again.
This may not be a very big problem. However, if it is compiled to exe file, it may cause sudden exit without warning and this is not a good user experience. So is there any way to stop the freeze?
This is part of the codes in which the window freezes.
class Success(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Yay! You have passed my chemistry challenge! Would you like to continue?",
font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = tk.Button(self, text="Continue", command=lambda: [controller.show_frame(Credits), success1()])
button1.pack()
button2 = tk.Button(self, text="Quit", command=lambda: controller.destroy())
button2.pack()
def correspondingBehavior(self, choice):
print(choice)
class Credits(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self,
text="Credits: bababa..." )
label.pack(pady=10, padx=10)
button1 = tk.Button(self, text="Quit", command=lambda: controller.destroy())
button1.pack()
def correspondingBehavior(self, choice):
print(choice)
Before the codes are written, I have imported playsound module and defined the success1() function like this:
def success1():
playsound("D:/Personal/Game/Yah.mp3")
Don't let it block the main thread:
def success1():
playsound("D:/Personal/Game/Yah.mp3", block=False)
Or you could create a new thread but that could potentially crash tkinter later.

tkinter - undock frame and handle window close event

Based on this question, I wrote the following mwe:
import tkinter as tk
class BaseFrame(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.bmanage = tk.Button(self, text='undock', command = self.undock)
self.bforget = tk.Button(self, text='dock', command = self.dock)
self.bmanage.grid(row=0, column=0, padx=20, pady=20, sticky='nsew')
self.bforget.grid(row=0, column=1, padx=20, pady=20, sticky='nsew')
self.dockable_frame = tk.Frame(self, bg="red", height=100)
self.dockable_frame.grid(row=1, column=0, padx=20, pady=20, columnspan=2, sticky='nsew')
self.label = tk.Label(self.dockable_frame, text="hi")
self.label.grid(row=0, column=0, padx=150, pady=20, sticky='nsew')
def undock(self):
self.parent.wm_manage(self.dockable_frame)
self.dockable_frame.configure(bg='blue')
print(type(self.dockable_frame))
def dock(self):
self.parent.wm_forget(self.dockable_frame)
self.dockable_frame.grid()
if __name__ == "__main__":
root = tk.Tk()
base_frame = BaseFrame(root)
base_frame.grid(row=0, column=0, padx=20, pady=20, sticky='nsew')
root.mainloop()
By clicking the undock button, the red frame is undocked and by clicking the dock button, the frame is docked again. I have two questions:
Why is the type of self.dockable_frame a <class 'tkinter.Frame'> and not a TopLevel since the wm manage documentation says: The widget specified will become a stand alone top-level window?
How can I handle the window close event since self.dockable_frame.protocol("WM_DELETE_WINDOW", insert_function_here) gives an error on my Windows pc?
The error is:
AttributeError: 'Frame' object has no attribute 'protocol'
I understand the error but how to handle the window close event?
The documentation is missleading. As I discovered this feature I thought simliar, the frame becomes a window. In fact that isnt really true which I can proof by my code below.
What really happens, at least under MS-Windows but I expect same functionality under other os, is that frames will be just packed on a different toplevel that will be created by wm_mange for this.
When tkinter defines a Window/Toplevel it always build a child window (frame) for the client area which you will work with. Thats why you need to call the win32gui.GetParent method when you will change your window style.
Code:
import tkinter as tk
import win32gui
def info():
print(f'python id: {id(frame)}')
print(f'tkinterID: {frame.winfo_id()}')
print(f'parent id: {win32gui.GetParent(frame.winfo_id())}')
def undock():
root.wm_manage(frame)
def forget():
root.wm_forget(frame)
frame.pack()
root = tk.Tk()
frame= tk.Frame()
frame.pack()
b1 = tk.Button(frame,text='INFO',command=info)
b2 = tk.Button(frame,text='mnge',command=undock)
b3 = tk.Button(frame,text='nrml',command=forget)
b1.pack()
b2.pack()
b3.pack()
root.mainloop()
Output:
By first appearance:
python id: 67118160
tkinterID: 3412074
parent id: 7867926
after undock
python id: 67118160
tkinterID: 3412074
parent id: 15666896
after forget
python id: 67118160
tkinterID: 3412074
parent id: 7867926
reference:
In Tk, Toplevel windows are basically a special form of a Frame which
are managed by the window manager. The proposal is to add the commands
wm manage and wm forget which will take an arbitrary Frame and allow
it to be managed by the window manager, making it a Toplevel window.
Why is the type of self.dockable_frame a <class 'tkinter.Frame'> and not a TopLevel since the wm manage documentation says: The widget specified will become a stand alone top-level window?
I think it is because self.dockable_frame is a python class and doesn't know that the underlying widget has been changed. Arguably this is a bug in wm_manage.
How can I handle the window close event since self.dockable_frame.protocol("WM_DELETE_WINDOW", insert_function_here) gives an error on my Windows PC?
The simplest way is to call the method directly from the tk.Wm class. It would look like this:
tk.Wm.protocol(self.dockable_frame, "WM_DELETE_WINDOW", self.whatever)

How to get the text out of a scrolledtext widget?

I am new to the tkinter python module. I try to do a project. I learned something new about the menus and I'm trying to make a little UI project that allows the user to write something in a scrolled text widget and then save it (using the sys module).
I've already tried some things that worked at buttons. For example .get() but it didn't work. I also tried the ["text"] method.
import tkinter, sys
root = tkinter.Tk()
class saveProject:
def __init__(self, master):
self.master = master
self.textFrame = tkinter.scrolledtext.ScrolledText(self.master, width=100, bd=10, relief="raised")
self.textFrame.pack()
def save(self):
#self.saveText = self.textFrame.get()
self.saveText = self.textFrame["text"]
project = saveProject(root)
root.mainloop()
The problem is, as I already said, I don't know how to get the text out of a tkinter.scrolledtext.ScrolledText widget.
So, out of curiosity I tried what described here (same link in my comment to the OP question). It works also for the scrolledtext.
import tkinter, sys
from tkinter import scrolledtext
root = tkinter.Tk()
class saveProject:
def __init__(self, master):
self.master = master
self.textFrame = scrolledtext.ScrolledText(self.master, width=100, bd=10, relief="raised")
self.textFrame.pack()
self.saveb = tkinter.Button(self.master, text="Save", command= lambda : self.save())
self.saveb.pack()
def save(self):
cur_inp = self.textFrame.get("1.0", tkinter.END)
fl = open("output.txt", "w")
fl.write(cur_inp)
project = saveProject(root)
root.mainloop()
I've added a save button at the bottom of the ScrolledText widget. The widget content is saved inside the output.txt area.
help(ScrolledText) indicates it's a subclass of the tkinter.Text widget, which apparently means that the way to get the text from it is the same — via its get() method using "Text widget indices" (here's some documentation about them).
Below is an example that gets all of the text in the widget (I added a Save text Button to test the save() method):
import sys
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
class SaveProject:
def __init__(self, master):
self.master = master
self.textFrame = ScrolledText(self.master, width=100, bd=10, relief="raised")
self.textFrame.pack()
# Added for testing.
self.save_btn = tk.Button(self.master, text='Save text', command=self.save)
self.save_btn.pack()
def save(self):
self.saveText = self.textFrame.get('1.0', tk.END) # Get all text in widget.
print('self.saveText:', self.saveText)
root = tk.Tk()
project = SaveProject(root)
root.mainloop()

tkk checkbutton appears when loaded up with black box in it

I create a check button / box, with the following call
x=ttk.Checkbutton(tab1,state='disabled',command = lambda j=i,x=k: fCheckButton(j,x))
x.state(['selected'])
The box appears fine and is selected, but it appears on load up, with a black box in it, which seems to have nothing to do with the state of it.
I have looked for reasons why, but can't actually find anyone with the same problem.
thanks
I hit this problem when creating a Checkbutton object from within a class. I was declaring a local variable instead of a member variable in the class. The local variable was getting out of scope causing the checkbox value to not be either a 0 or a 1.
Wrong:
import tkinter as Tk
from tkinter import IntVar
from tkinter.ttk import Frame, Checkbutton
class TestGui(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
var1 = IntVar()
var1.set(1)
button = Checkbutton(parent,
text="Pick me, pick me!",
variable=var1)
button.grid()
root = Tk.Tk()
app = TestGui(root)
root.mainloop()
Fixed:
import tkinter as Tk
from tkinter import IntVar
from tkinter.ttk import Frame, Checkbutton
class TestGui(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.var1 = IntVar()
self.var1.set(1)
button = Checkbutton(parent,
text="Pick me, pick me!",
variable=self.var1) # note difference here
button.grid()
root = Tk.Tk()
app = TestGui(root)
root.mainloop()
I've had a similar issue on Windows 7.
After loading the app, one of my checkbuttons contained a filled square. But after clicking on it, it became a normal checkbutton:
In my case, it was because I had multiple checkbuttons sharing the same variable... After creating a separate Tk.IntVar() variable for each checkbutton, the problem disappeared.
import Tkinter as Tk
import ttk
root = Tk.Tk()
checkVar = Tk.IntVar()
x = ttk.Checkbutton(root, variable=checkVar, text="check 1")
x.pack()
checkVar2 = Tk.IntVar()
y = ttk.Checkbutton(root, variable=checkVar2, text="check 2")
y.pack()
root.mainloop()

Entry widget contents not changing with function call

I need to change the content of an entry whenever the tkinter frame is shown. Below is what I have so far, and it doesn't seem to work. I have tried to use data = self.read() and then now.insert(0, data) and that has not worked either. If the value is displayed then it doesn't get changed every time the class ReadLabel1 is called.
class ReadLabel1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent, bg="blue")
label = tk.Label(self, text="SomeData:", font = "Times 12", bg="blue")
label.place(x=10, y=100) #ack(pady=5,padx=30)
self.smStr = tk.StringVar()
now=tk.Entry(self, width=22, textvariable=self.read())
now.place(x=120, y=103)
def read(self):
# code to get data
return data
You need to turn 'change the content of an entry' into a one parameter callback, turn 'whenever the tkinter frame is shown' into an event, and then bind together the app, the event, and the callback. Here is a minimal example.
import time
import tkinter as tk
root = tk.Tk()
now = tk.StringVar()
lab = tk.Label(root, textvariable=now)
lab.pack()
def display_now(event):
now.set(time.ctime())
root.bind('<Visibility>', display_now)
root.bind('<FocusIn>', display_now)
Minimizing the window to a icon and bringing it back up triggers the Visibility event. Covering and merely uncovering with a different window did not, at least not with Windows. Clicking on the uncovered, or merely inactivated, window triggered FocusIn. You can experiment more with your system. I used this tkinter reference

Categories

Resources