Adding a button to align with ttk notebook caused a geometry problem - python-3.x

I have found out a method to get a button to align with ttk.Notebook.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
notebook = ttk.Notebook(root)
ttk.Button(notebook).pack()
notebook.pack(fill='both', expand=1)
label = ttk.Label(notebook, text='Text', font='Arial 50')
notebook.add(label, text='Tab')
root.mainloop()
However, this caused a problem that the geometry manager only displays the button. I have to maximise the window in order to see all the contents.
In my another bigger gui, this is just a little bit of the window. I can’t see the content inside the notebook even I maximise the window.
So, how can I get it working? Thanks for any ideas!

This is due to the fact that you're calling pack on a button inside the notebook. The button is empty so it has a size of zero, and calling pack on the button causes the notebook to shrink to the size of the button.
You shouldn't ever call pack or grid on widgets that are direct children of a notebook, they should only ever be added with the add method of the notebook.
It's not clear where you want the button, but it shouldn't be a child of the notebook. If you make it a child of the root window (eg: ttk.Button(root), you can add it to the root window with pack to get it to be either above, below, or to one of the sides of the notebook.

Related

How to fix tkinter cursor vertical offset while in full screen on macOS?

I am having a problem with Tkinter detecting the wrong cursor position when the application is in fullscreen mode on macOS.
I have created a Python 3 application with a Tkinter GUI on macOS (Mojave). The application works well until the green full screen button is pressed. After this the menu bar is hidden as expected, but the window only expands to 2560x1395 on a 2560x1440 display. The interface also detects the mouse cursor above where it appears on the screen, which makes the application impossible to use. I have tried forcing the application to expand to the full height of 1440, but it immediately changes back to 1395.
Here is a minimal case demonstrating the problem:
import tkinter as tk
def test():
print("Test")
root = tk.Tk()
tk.Button(root, text="Test", width=20, command=test).pack()
root.mainloop()
While in normal windowed mode, clicking the button causes "Test" to be printed. While in full screen the user must click below the button for the click to be registered. Exiting the application while in full screen mode also causes a segmentation fault.
In my application, clicking where the red dot is causes the OptionMenu to open:
The applications which support macOS properly opens in a separate Desktop tab when used in fullscreen. As far as I observed this is not case with tkinter, the window expands to fit completely with the screen size onto the main Desktop tab.
Why there is an offset when in fullscreen mode?
The offset is of the titlebar which hides in fullscreen mode but doesn't register and shifts all the widgets accordingly. That's why mouse clicks register + 24 (height of the titlebar) from the click. To avoid this we use root.attributes('-fullscreen', 1).
To exit fullscreen we can use <Escape> bind as Escape key in most application is to exit from fullscreen like in Safari. You can also use a Button.
root.bind('<Escape>', lambda e: root.attributes('-fullscreen', 0))
I couldn't find an event handler for fullscreen mode on Mac so I used <Configure> with condition root.attributes('-fullscreen') to check if the user clicked for fullscreen or not.
This is what I come up with.
from tkinter import *
root = Tk()
Button(root, width=20, text='Hello').pack()
def full_screen(evt=None):
# checks if the window is in fullscreen
if root.attributes('-fullscreen'):
root.attributes('-fullscreen',1)
# Remove the borders and titlebar
root.overrideredirect(True)
root.overrideredirect(False)
root.bind('<Configure>', full_screen)
# Escape bind to exit fullscreen.
root.bind('<Escape>', lambda e: root.attributes('-fullscreen', 0))
root.mainloop()
This issue is known for 6 months now, and Apple is doing nothing about it, now the fun fact is the logic display is fine (if you record your screen or if you screen share it doesn't happen) it only happens on the physical screen, and it only happens on MBP 16"
I am now over the fact that there is a hardware issue with the (some) MBP 16" video cards and Apple is just playing dumb.
If it helps, having your screen without scale or with "double" scale works fine, it's in the 3 intermediate scales that the issue presents itself

Tkinter - hide root window

I'm new to stack exchange and am inexperienced with python. I am using python3 and have used SourceFileLoader from importlib.machinery to run another script(which uses tkinter) from my main script. This initially caused an error covered in this question: tkinter.TclError: image "pyimage3" doesn't exist
The solution worked for me in resolving the error, and now the child script runs as intended, except that using the Toplevel() function now also causes the app to create an empty root window - Tk() which I want to hide. I have looked at a number questions' solutions that have not worked: How do I get rid of Python Tkinter root window?
Hide the root window when a Toplevel window is opened and make it reappear when the Toplevel is destroyed
Here is a sample of my code:
from tkinter import *
from PIL import ImageTk
from importlib.machinery import SourceFileLoader
root = Toplevel()
background = Tk()
...
def Puzzle5():
root.overrideredirect(1)
frame = Frame(root, width=320, height=160, borderwidth=2, relief=RAISED)
frame.pack_propagate(False)
frame.pack(side=TOP)
frame1 = Frame(root, width=500, height=150, borderwidth=2, relief=RAISED)
frame1.pack_propagate(False)
frame1.pack(side=BOTTOM)
image = ImageTk.PhotoImage(file="/home/pi/Media/arrowup.png")
image1 = ImageTk.PhotoImage(file="/home/pi/Media/arrowdown.png")
...
background.withdraw()('0x0+0+0')
root.geometry('644x450+150+50')
root.mainloop()
def close():
root.destroy()
background.destroy()
Puzzle5()
Widgets exist in a hierarchy. At the top of that hierarchy is the root window. For any tkinter widget to exist, there must first be a root window.
You can create the root window by creating an instance of Tk. If you do not, then the first time you create a window a root window will be created for you.
Now, consider this code:
root = Toplevel()
background = Tk()
Toplevel is not a root window. For it to exist, there must first be a root window. Since you did not create one, tkinter will create one for you. So, you get a root window, and then you get the instance of Toplevel.
Then you create another root window with the second line, resulting in three windows. Even when you hide background with background.withdraw(), you still have the original root window visible.
The simple solution is to reverse those two lines of code. Create the root window first, and the Toplevel second. Then you only have a single root window, and you can hide it if you wish. However, as the answer to How do I get rid of Python Tkinter root window? explains, an even better solution is to not use a Toplevel at all, but instead put your widgets in root.

Make a Toplevel window take up the entire monitor space?

I'm trying to make a tkinter Toplevel window to go full screen and take up the entire monitor space (going over the task bar as well). I am able to do this with the Tk() using the code: window.attributes("-fullscreen", True). But I know you can't have two instances of Tk() so I need to use a Toplevel and root.attributes("-fullscreen", True) does not work with Toplevel.
The code I have in place at the moment to make the Toplevel fullscreen is this:
window = Toplevel()
w = window.winfo_screenwidth()
h = window.winfo_screenheight()
window.geometry("%dx%d+0+0" % (w,h))
But this does not even go into a proper fullscreen mode as the left side of the interface does not reach the edge of the screen.
How can I get a Toplevel window to take up the entire space of the screen like a Tk() window can?
I found a work around to this issue. What I did was I set the Tk() interface to take up the entire screen, and I placed a frame on top of it and made it fit the Tk(). That way I could put the desired widgets on the frame and when I'm done, destroy the frame and reset the size of the Tk() frame.
This is a possible way to solve fullscreen trouble:
finestra1= Toplevel()
wf1= finestra1.winfo_screenwidth()
hf1= finestra1.winfo_screenheight()
A = str(wf1)
B = str(hf1)
finestra1.geometry(A+"x"+B)

Closing a tkinter Entry Widget without killing the program

I am currently stepping through the tkinter tutorial at python-course.eu. Is it possible to close an entry widget without killing the program? What I am trying to do is incorporate the tkinter entry box into a pygame program such that the program asks for the players name via an entry box and then closes once the text has been entered. The game should then continue. Is this possible?
What I would like to do is:
-create a pygame surface
-open a tkinter entry widget on top of that surface
-get the users name
-close the tkinter widget
-write text using pygame onto the surface that incorporates the user's name
What is stumping me is the fact that the tkinter examples on python-course.eu all end with a mainloop() statement while I would like pygame to have an event loop so that I can expand the program. I anticipate that the widget creation would occur prior to dropping into the event loop. This is where I am stuck :-(
rather than trying to mix two GUI libraries in the same window, if a prompt is acceptable you could use:
import tkinter as tk
from tkinter import simpledialog
root = tk.Tk() # needed to prevent extra window being created by dialog
root.withdraw() # hide window as not needed
username = simpledialog.askstring('Username', 'Enter username:')
root.destroy()

Python Tkinter Clickable Text?

I'm wondering if there's a way to make clickable text in Tkinter. Maybe like you would see on a title screen of a game, and where you hover your mouse over the text and it changes color/hightlights itself. All I need the click to do is execute another function.
Are either of these things possible? Thanks!
you are looking for tkinter's events:
tk_widget.bind("<Button-1>",CALLBACK)
The call back needs to take an event argument which is a dictionary containing information about what triggered the event.
This can run into issues with widgets that overlap such as windows in a canvas or labels sometimes triggering the callback for the window behind it.
For hovering the mouse over a widget the event is called "<Enter>" and moving mouse out of widget region is called "<Leave>" for highlighting text effect, if you just want to capture a click anywhere on a window then on the root call root.bind_all("<Button-1>",CALLBACK)
source: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/events.html
example:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
def change_case(event=None):
new_text = str.swapcase(lab["text"])
lab.config(text=new_text)
def red_text(event=None):
lab.config(fg="red")
def black_text(event=None):
lab.config(fg="black")
root = tk.Tk()
lab = tk.Label(root,text="this is a test")
lab.bind("<Button-1>",change_case)
lab.bind("<Enter>",red_text)
lab.bind("<Leave>",black_text)
lab.grid()
root.mainloop()
hope this helps :)

Resources