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

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

Related

Make Tkinter Notebook draggable

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.

Python Tkinter side notebook tabs

I am redesigning the GUI of a program that uses tkinter in python. I used ttk widgets for this program, but I think, even on W10, the interface is way too old so I decided to update the visual interface for the program using METRO interface or W10 alike UI.
The first thing that come in mind, is that W10 have a left-side "tabs" that are very beautiful and useful, and the question is if is that a way to using the ttk.notebook widget, change the position of the tabs?
Otherwise, I could do buttons placed on the side and load frame widgets on every button clicked, but I think this could overload so much the program loading constantly frames and widgets, and I am trying to avoid this way.
Thanks to everyone.
It is possible to change the position of the tabs by configuring the tabposition option of the TNotebook style.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
style = ttk.Style(root)
style.configure('lefttab.TNotebook', tabposition='ws')
notebook = ttk.Notebook(root, style='lefttab.TNotebook')
f1 = tk.Frame(notebook, bg='red', width=200, height=200)
f2 = tk.Frame(notebook, bg='blue', width=200, height=200)
notebook.add(f1, text='Frame 1')
notebook.add(f2, text='Frame 2')
notebook.pack()
root.mainloop()
This is how it looks like with the default theme on linux:
However, the text inside the tabs is always horizontal. I don't have Windows, so I don't know exactly how the W10 UI looks like, but I guess that you would like to rotate the tabs, not just change there position and I don't know how to do that.

Tkinter menu bindings and accelerators

I've come across a weird problem and I can't work out what is happening. I am working on a Tkinter application which utilises a menu. I have found that while creating the menu, in which all items have accelerators, some items require additional bindings to make the accelerators work (unless the menu bars are already selected) and some don't.
When an additional binding is required, I have a problem with double entry key strokes. For example, if I open a Toplevel window, I get double entries every time I type a character in an Entry box, both in the Toplevel and in the main window. This only happens if the menu item is called via the key command.
Most of the time this is not a problem, although I'd really like to know what the underlying cause is because it just seems wrong, but this particularly came to my attention recently when I implemented the built-in OS X Preferences menu, using the following code:
self.window.createcommand('::tk::mac::ShowPreferences', self._settings)
Now when I call the settings function from the build-in Preferences key command Command-,, which instantiates a new Toplevel window, this double entry is what happens. It does not happen if I navigate to the menu and open it with the mouse.
The example below recreates the problem for me. The menu bar is not strictly necessary, but the problem occurs with both the menu bar and the OS X built-in Preferences item. Interestingly, 'Settings A' which does not require a binding reproduces the problem, but 'Settings B', which does require a binding, works fine. And again, only with key commands.
import Tkinter
def settings(event = None):
top = Tkinter.Toplevel()
Tkinter.Entry(top).pack()
top.mainloop()
root = Tkinter.Tk()
root.createcommand('::tk::mac::ShowPreferences', settings)
menuBar = Tkinter.Menu(root)
fileMenu = Tkinter.Menu(menuBar)
fileMenu.add_command(label = 'Settings A', accelerator = 'Command-Shift-a', command = settings) # Does not require binding
fileMenu.add_command(label = 'Settings B', accelerator = 'Command-b', command = settings) # Requires binding
menuBar.add_cascade(label = 'File', menu = fileMenu)
root.config(menu = menuBar)
root.bind('<Command-b>', settings)
Tkinter.Entry(root).pack()
root.mainloop()
I suspected it may be a computer issue but I have tried it on another machine and I get the same result. Does anybody have any idea what is happening here and how I can prevent it?
In case anybody's interested, I think I've got to the bottom of this. I believe it was caused by the version of tkinter I had. Today I updated to Python 3 from the OS X bundled version 2.7, and the problem remained. Then I updated tkinter to ActiveTcl 8.5.18.0 and the problem seems to have disappeared.
IDLE and tkinter with Tcl/Tk on macOS

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.

tkinter menu accelerators and modal dialog boxes

I want to have a menu that shows a modal dialog box. Everything is fine, until I add an accelerator. If I do this and use the accelerator to access to dialog, it hangs. I suspect that wait_window, used inside the modal dialog box is somehow in conflict with the mainloop, when called from a "bind". Here is an example:
import tkinter
from tkinter import simpledialog
class App(tkinter.Tk):
def __init__(self):
tkinter.Tk.__init__(self)
self.bind_all("<Control-f>", lambda event: self.menu_file())
menubar = tkinter.Menu(self)
fileMenu = tkinter.Menu(menubar, tearoff=False)
fileMenu.add_command(label="File", underline=0,
command=self.menu_file, accelerator="Control+f")
# fileMenu.add_command(label="File", underline=0,
# command=self.menu_file)
menubar.add_cascade(label="File",underline=0, menu=fileMenu)
self.config(menu=menubar)
def menu_file(self):
simpledialog.Dialog(self,"Message")
app=App()
app.mainloop()
If in the above code I comment out the line that adds the accelerator and uncomment the subsequent line, there is no hangup (I can of course still use Ctrl+F to access the dialog box). The only problem is that the accelerator string is not shown next to the File menu. According to the tkinter documentations on the web that I have found, adding the accelerator should only change how the menu is shown and nothing else, so I am really puzzled. Anyone any ideas? (I could of course emulate accelerators by modifying the strings to be displayed, but I would not consider this as an elegant solution.)
As I have noticed, this is a Mac-specific bug.
Following the workaround suggested for a known Tk bug on Mac (see link), changing the line which binds the menu method to the keystroke to the following:
self.bind_all("<Command-f>", lambda event: self.after(100,self.menu_file))
is "fixing" the bug. They also suggest to increase 100 to 150 on slower systems. Hmm..

Categories