Make Tkinter Notebook draggable - python

A similar question was asked back in '15 Make Tkinter Notebook be Draggable to Another View but that was a while ago and that also asked about re-binding the window.
I was wondering how I would make a notebook draggable, even if is just to reorder the tabs.
Any advice would be helpful and please let me know if this question has been answered

Tab dragging has been implemented in TCL: https://wiki.tcl-lang.org/page/ttk::notebook, https://wiki.tcl-lang.org/page/Drag+and+Drop+Notebook+Tabs. It can either be translated to python or evaluated through .tk.eval().
For the second solution, one can put the TCL tab dragging code from the first link (except the last block which creates an example notebook) in a string and evaluate it with root.tk.eval(TCL_CODE). Subsequently created notebooks will have tab dragging:
import tkinter as tk
from tkinter import ttk
tcl_code = """
put here the code from the dragging tab code section of https://wiki.tcl-lang.org/page/ttk::notebook
"""
root = tk.Tk()
root.tk.eval(tcl_code)
nb = ttk.Notebook(root)
nb.pack()
for i in range(10):
nb.add(ttk.Frame(nb, width=100, height=100), text=f"Tab {i}")
root.mainloop()
I also implemented tab dragging with animations (seeing the dragged tab move) as part of a larger project, the source code is available here.

Related

How to bring Tkinter window in front of other windows?

I'm working with some Tkinter Python code (Python 3.4), and I've come across a problem. When I create my Tkinter window it doesn't show up in front. I do it currently with the following code:
from tkinter import *
win = Tk()
win.minsize(width=1440, height=828)
win.maxsize(width=1440, height=828)
The minsize() and maxsize() make the window cover my entire screen, but the original python running window (The one that wouldprint("Hello, World!")) ends up on top. Is there a way to fix this? I'm running OS X 10.10.1.
Set it as the topmost (but it will always stay in front of the others):
win.attributes('-topmost', True) # note - before topmost
To not make it always in front of the others, insert this code before the mainloop:
win.lift()
win.attributes('-topmost', True)
win.attributes('-topmost', False)
Don't forget win.mainloop() at the end of your code (even if in some cases it's not explicitly required)
Other discussions on the same problem:
How to put a Tkinter window on top of the others
How to make a Tkinter window jump to the front?

ttk OptionMenu does not fit into its bounding box

I am facing a problem creating a Tkinter-application under Windows, using python 2.7. Basically, when I create an OptionMenu, its right corner (where a down button indicates that something happens when you click there) is truncated in the middle.
The following code reproduces the issue:
from Tkinter import Tk, StringVar
from ttk import OptionMenu
root = Tk()
options = list('ABC')
var = StringVar(value='A')
om = OptionMenu(root, var, var.get(), *options)
om.config(width=25)
om.pack()
root.mainloop()
The result looks on my computer like this:
I have played around with the padx and ipadx keywords of the packing layout manager and also tried a grid layout instead. None of them lets me see the down-arrow completely.
I appreciate your helpful comments on this issue.
The same happens to me on Windows 7 but not on XP, both using Python 2.7. I have found a bug report which states is should be fixed in Tk 8.5.8. Updating Tcl/Tk in Python seems to be very complicated though
The fix in question is for one of the script files shipped in the tk library. You could modify your local copy of vistaTheme.tcl to match this. In later versions I think it does actually request the size from the system properly but this should work if you are forced to use an older version of Tk.
You can find the path using:
from Tkinter import Tk
tk = Tk()
tk.eval("set tk_library")
and then edit the /ttk/vistaTheme.tcl file. I've got python3 here and it seems to have come with Tk 8.6.1 so has this fixed already.

Event handler for left-click on Notebook tabs in Tkinter

In Tkinter, I want to make it so when I click one or more times on an already open/selected tab of a Notebook object so that it does not take the focus (or so that it gives the focus back to the Text widget in the window).
How do I do this?
If there's an event handler for clicking on tabs, that would more than suffice.
Also, if there's an ability to make it so widgets will take events, but not take the focus, that would be great.
I already know about the virtual event <<NotebookTabChanged>>. However, I'm talking about when you click on an already selected tab. So, the tab isn't changing. I tried just binding the <Button-1> event to the Notebook widget, but it didn't do anything.
Since making Notebooks isn't common knowledge to everyone who uses Tkinter, here's an example of how to make a minimal Notebook with tabs. I don't know why the text is cut off in the final tab here, though (but it's not in my full code):
from tkinter import *;
from tkinter.ttk import *; #Notebook comes from this
class Editor:
def __init__(self):
self.tk=Tk();
self.tabs=0;
self.frame=Frame(self.tk);
self.nb=Notebook(self.frame);
self.frame.pack();
for x in range(5):
self.add_tab();
self.nb.pack();
self.tk.mainloop();
def add_tab(self):
newTabFrame=Frame(self.nb);
text=Text(newTabFrame); #Just a sample Text widget to go in each tab
text.pack();
if self.tabs==0:
self.nb.add(newTabFrame, text=str(self.tabs), compound=TOP);
else:
self.nb.add(newTabFrame, text=str(self.tabs));
self.tabs+=1;
if __name__ == '__main__':
e=Editor();
try:
e.tk.destroy();
except:
pass;
Okay, obviously I was doing something wrong, because when I tested out what I thought I already tested out on my larger code on the example code I used in my question, it actually worked. Then, I discovered that this works on my larger code, too. Huh. I think I must have been using bind_all instead of bind (whereas I thought I had attempted both), because that really doesn't work. EDIT: Actually, I was attempting to use it without return "break" and while that partially works, it doesn't work after you click on a new tab when you directly click on it again. Plus, setting the focus in my answer here with return "break" won't solve the problem (although it does answer my question), because it will set the focus on the current tab before it switches tabs. I'll figure something out, I'm sure. EDIT: Changing Button-1 to ButtonRelease-1 (or some such) fixes the problem. The Motion binding with the same handler, has potential, too, but if you have Toplevel windows popping up, this will take the focus from them if you move the mouse pointer over your tab.
Here's the working code:
from tkinter import *;
from tkinter.ttk import *; #Notebook comes from this
from tkinter.messagebox import *;
class Editor:
def __init__(self):
self.tk=Tk();
self.tabs=0;
self.frame=Frame(self.tk);
self.nb=Notebook(self.frame);
self.nb.bind("<ButtonRelease-1>", self.test);
self.frame.pack();
for x in range(5):
self.add_tab();
self.nb.pack();
self.tk.mainloop();
def add_tab(self):
newTabFrame=Frame(self.nb);
text=Text(newTabFrame);
text.pack();
if self.tabs==0:
self.nb.add(newTabFrame, text=str(self.tabs), compound=TOP);
else:
self.nb.add(newTabFrame, text=str(self.tabs));
self.tabs+=1;
def test(self, event=None):
showinfo("Success", "It works!");
#Imagine the code for selecting the text widget is here.
return "break";
if __name__ == '__main__':
e=Editor();
try:
e.tk.destroy();
except:
pass;

how to enable & disable tabs in a Tkinter/Tix Python GUI

I'm trying to make a tabbed GUI in Python and I want to be able to toggle the enabled/disabled state of the tabs (i.e. prevent the user from switching tabs, and ghost non-active tabs out to make this fact obvious). So far I've been unable to figure out how to do this state toggling.
I've decided to go with Tkinter and/or Tix because they come built into Python distros on Windows, (guiding my users through installing extra third-party dependencies will be more trouble than it's worth). I've worked with Tkinter a bit but never Tix until now-tabs seem to require it. So I've built a two-tabbed Tix.NoteBook based on the demo at http://svn.python.org/projects/python/trunk/Demo/tix/samples/NoteBook.py
For disabling a tab, the only relevant attribute of the Tix tab instance (e.g. nb.hard_disk in the demo code) seems to be configure() but naively doing something Tkinter-like, i.e. nb.hard_disk.configure(state=Tix.DISABLED), results in TclError: unknown option "-state"
Searches for "disable Tix notebook tab" yield nothing, and even the more general "disable Tix widget" yields nothing I can understand/use. Grateful for any pointers in the right direction.
In general how you disable widgets in Tkinter is by setting the "state" option to Tk.DISABLED or more foolproof just setting it to a string saying "disabled". The following grays out and disables your tab:
notebook.tab(0, state="disabled")
with 0 being the index of the tab you want to disable, and notebook being your notebook object. Does that answer your question?
Below is a simple notebook example to demonstrate:
import Tkinter
import ttk
window = Tkinter.Tk()
notebook = ttk.Notebook(window)
notebook.pack()
subframe = Tkinter.Frame(window)
subframe.pack()
notebook.add(subframe, text="tab", state="normal")
def buttonaction():
notebook.tab(0, state="disabled")
button = Tkinter.Button(subframe, command=buttonaction, text="click to disable tab")
button.pack()
if __name__ == "__main__":
window.mainloop()
This might be what you are looking for:
nb.pageconfigure('hard_disk', state=Tix.DISABLED)
http://tix.sourceforge.net/dist/current/man/html/TixCmd/tixNoteBook.htm#M27

Determining what tkinter window is currently on top

I have written an application in python 2.7 and tkinter. I created a tool bar with several buttons that open up respective top windows that display various options. I used ttk.Checkbutton with the 'toolbutton' style as an indicator to show whether the option windows are open or closed.
The problem is that the option windows will go to the back if another window is selected. Currently, if one selects the toolbutton again, the option window will close. However, I only want to close the window if it is on top. If the option window is not on top, I want the window to moved to the front.
Some of the code I have working:
class MainWindow:
def __init__(self,application):
self.mainframe=tk.Frame(application)
application.geometry("900x600+30+30")
self.otherOptionsSelect=tk.IntVar()
self.otherOptions_Button=ttk.Checkbutton(application,style='Toolbutton',variable=self.otherOptionsSelect,
onvalue=1, offvalue=0,image=self.optionsIcon, command=self.otherOptions)
def otherOptions(self):
if self.otherOptionsSelect.get()==0:
self.otherOptions.destroy()
return
self.otherOptions=tk.Toplevel()
self.otherOptions.title("IsoSurface Options")
self.otherOptions.geometry("200x165+"+str(int(application.winfo_x())+555)+"+"+str(int(application.winfo_y())+230))
self.otherOptApply_button=ttk.Button(self.otherOptions,text="Apply",command=self.showFrame)
self.otherOptApply_button.place(x=20,y=80,width=50,height=30)
self.otherOptClose_button=ttk.Button(self.otherOptions,text="Close",command=self.otherOptionsClose)
self.otherOptClose_button.place(x=80,y=80,width=50,height=30)
def otherOptionsClose(self):
self.otherOptionsSelect.set(0)
self.otherOptions.destroy()
Here is a picture of the entire application I have written:
In the above image, each window has their respective ttk.checkbutton. At the moment, toggling the checkbutton either opens or closes the window. However, what I really want it to do is close the window if the window is in front of the application, or bring the window to the front if it is behind the application.
Hopefully this clears some things up.
Thanks in advance!
It is in fact possible to check stacking order of windows. Using Tkinter, you have to do some funny tcl evals to get at the information. I found the answer at TkDoc in the section on Windows and Dialogs, scroll down until you get to "Stacking Order". The code baffled me until I started playing around with it interactively. My test code was:
import Tkinter as tk
root = tk.Tk()
root.title('root')
one = tk.Toplevel(root)
one.title('one')
two = tk.Toplevel(root)
two.title('two')
I then manipulated the windows so that two was on top, one under that and root below them all. In that configuration, the following weirdness can tell you relative layering of windows:
root.tk.eval('wm stackorder '+str(two)+' isabove '+str(root))
returns 1, meaning "Yes, window two is above window root." While the following:
root.tk.eval('wm stackorder '+str(root)+' isabove '+str(two))
returns 0, meaning "No, window root is not above window two." You can also use the command:
root.tk.eval('wm stackorder '+str(root))
Which gives back the full window stacking order in the form of a weird string something like this:
'. .68400520L .68401032L'
Which starts to make sense when you run the commands:
str(root)
str(one)
str(two)
and figure out that root has the internal name '.', one is '.68400520L' and two is '.68401032L'. You read the output of root.tk.eval('wm stackorder '+str(root)) backwards so it's saying two is on top, one is under that and root is below both.

Categories