Event handler for left-click on Notebook tabs in Tkinter - python

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;

Related

Work with different tabs, of the Notebook tkinter

I have a question or problem, maybe there is an answer, but I still can't understand. I have an application, which works with several Notebook tabs
When I click on any button, it calls me a function that in turn works with a class where it creates me, the tab and the widgets:
def open_issuesDeviation (self):
global deviation deviation =
Deviation (self.root)
self.book.add (deviation, text = 'Issues DEVIATIONS')
self.book.select (deviation)
deviation.DESVfr1_entModulo.focus ()
There is even great, I do my tasks I can open,. Tabs and work on them:
My problem is that in the menu bar or in the contextual menu of the right click, when hitting search, call a simple function that does:
def search (self, event):
self.DESVfr1_entModulo.focus()
It is to activate the focus in the entry, when opening each tab and doing CTRL F, or searching in the menu, it manages to activate the focus, the problem is when I return to a previous tab, this no longer works for me, and I have seen that It is because perhaps in each tab, it will be created with a different name to the widget:
Tab 1:
.! Deviation.! Labelframe.! Entry
Tab 2:
.! Deviation2.! Labelframe.! Entry
I think I understand that the last tab is always active.
I have seen that there is a lot of talk about link tags, and I think it may be the solution but I don't understand how I can implement it in my case.

Tkinter: how to open a Toplevel window from another script

I'm a newbie in programming and I'm struggling a bit (a lot actually) with my first "program". Here's what I did:
since I want the code not to be too long, I want to split it in two files. In the first one I call the main window, where I put a button. If I click this button, I run the second script, that should hide the main window and make appear a new window (toplevel). The problem is that if I click the button nothing happens.
A thing I noticed is that if in the second script I add the strings "if name=="main": main.mainloop()", then this new window does appear, but a second main window appears too, as if I re-runned the main code.
Script one:
from tkinter import *
import os
main=Tk()
def sw():
os.system("python3 ./script_two.py")
my_button=Button(main,text="NEW",command=sw)
my_button.grid(row=0,column=0)
if__name__="__main__":
main.mainloop()
Script two:
from tkinter import *
import os
import script_one
script_one.main.withdraw()
second_window=Toplevel()
def Close():
second_window.destroy()
script_one.main.deiconify()
second_button = Button(second_window,text="Exit",command=Close)
second_button.grid(row=0,column=0)
If I put together the scripts (inserting the code of "script_two" in 'def sw()'), I get exactly what I want, but if I split them as showed, I get the problem.
Obviously for a code this short it would be easier and faster to write a single script, what I actually have is a longer code, but the problem is the same.
Any idea? Thank you very much

tkinter checkbutton not displaying correct value when class imported from other file

I have a main program which does some cool stuff and I am currently setting up a 'settings editor' to let the user change some GUI related stuff and default values. It reads values from a text file, which is read in correctly and saves them to a dictionary self.propertiesDict. Some of the options are on/off switches, so I use checkbuttons for them. What is puzzling me is following behavior: the code works perfectly fine, when I execute the settingsEditor.py (the script creating the settings window) directly. All the checkbuttons are set to active / True. However, when I include my settingsEditor in my main program and call it, it creates fine but all the checkbuttons show the wrong value: False. I read a lot of topics here to find an answer, but I think I avoided the most common errors:
I use the tk variables
tk variables are created and set prior to the buttons
variables are not only in local scope (prefixed self.)
As you can see, I tried with an IntVar and a BooleanVar, but neither is working correctly. Something else is strange, when I use ttk.checkbuttons, I get the issue described here. I use Visual Studio for debugging and I can't see any difference in the process when going trough line by line, except for the wrong display result. I am happy for any suggestion. Sorry for not providing a full MWE, I will do, if nobody can help me from this here.
settingsEditor.py
import tkinter as tk
from tkinter import ttk
...
class mySettingsEditor:
def __init__(self):
...
def createGUI(self):
# Show main options on startup on/off
self.showOptionsVar = tk.IntVar()
self.showOptionsVar.set(str2int(self.propertiesDict['showMainOptionsExpanded']))
print(self.showOptionsVar.get())
self.checkBtn1 = tk.Checkbutton(Frame, text='Main Options Section', variable=self.showOptionsVar)
self.checkBtn1.grid(column=0,row=2)
# Show main STL section on startup on/off
self.showMainSTLVar = tk.BooleanVar()
self.showMainSTLVar.set(str2bool(self.propertiesDict['showMainSTLSectionExpanded']))
print(self.showMainSTLVar.get())
self.checkBtn2 = tk.Checkbutton(Frame, text='Main STL Section', variable=self.showMainSTLVar)
self.checkBtn2.grid(column=0,row=3)
main.py
from settingsEditor import mySettingsEditor
...
settEditor = mySettingsEditor()
This is how it looks in the GUI when executed separately (terminal with print output to the left):
Thats the result when I add it in main.py. The boxes are unchecked, but .get() tells me the values are correctly assigned to the tk variables.
As suggested by jasonharper, switching to Toplevel() for the child windows fixed the issue. Thanks alot!

Blocking The Tab Key

I've recently made a text editor with tkinter for python.
I need a way to disable tab from being able to be used normally, so it doesn't indent.
Does anyone have any idea as to how I would achieve this?
Thank you for your time.
It really depends on exactly what you did. Without more information I'm going to assume that you have a text widget somewhere and that you want to disable tab from indenting there.
Example:
from tkinter import Tk, Text
def no_tab(event):
return 'break'
root = Tk()
text_widget = Text()
text_widget.pack()
text_widget.bind('<Tab>', no_tab)
root.mainloop()
In this example we bind the <Tab> key to the function no_tab. So everytime tab is pressed within the text widget the no_tab function is called. The no_tab function returns the magic string 'break' which means that the action of the key won't be preformed and thus disabling the indentation that the tab key would have created otherwise.

Python3 Tkinter popup menu not closing automatically when clicking elsewhere

I'm running Python 3.3.3 (and right now I'm on Ubuntu but I also develop on Mac and Windows, which I haven't yet tested). I have a Treeview object that responds to right click on items and shows a context menu depending on what you click... but I've noticed that if you right click somewhere else while the original menu is up, it just opens another one.
In fact, normal clicking doesn't hide them either. Even when I close the window the menus still stay floating. The only way to get them to go away is to click one of the options.
The end result is this:
My code for the menu is as follows:
def rightclick_listitem(self, event):
rowitem = self.sources.identify('item', event.x, event.y)
if rowitem == '':
print('Right clicked an empty space.')
return
# user right clicked something.
self.sources.selection_set(rowitem)
rcmenu = Menu(self.root, tearoff=0)
plugin_disabled=self.sources.item(rowitem, 'values')[0] == 'Disabled'
if plugin_disabled:
rcmenu.add_command(label='Plugin is disabled...',
command=self.plugin_disabled_click)
rcmenu.add_command(label='Plugin options',state='disabled' if plugin_disabled else 'active')
rcmenu.add_command(label='Uninstall plugin')
rcmenu.post(event.x_root, event.y_root)
The code that calls this code is located here:
#RIGHTMOUSE is a variable that changes based on OS due to the way Mac OSX works
#sources is the treeview object
self.sources.bind(RIGHTMOUSE, self.rightclick_listitem)
I googled around and only got some people asking the same question with no answers. I'm still somewhat new to tkinter and python in general, and didn't see anything about this. I bind other actions to the treeview as well.
If you need more sourcecode my project is here: https://github.com/Mgamerz/Fresh-Set-of-Images (freshsetofimages.py)
Any help is appreciated.
And the plugins required to make this appear: https://github.com/Mgamerz/fsoi_plugins
Try calling the method tk_popup rather than post.
Also, your code has a memory leak, in that each time you right-click you're creating a new menu but never destroying the old one. You only ever need to create one, and the reconfigure it before popping it up.
To close the popup menu when click elsewhere, you can add
rcmenu.bind("<FocusOut>",popupFocusOut)
and call unpost in popupFocusOut.
def popupFocusOut(self,event=None):
rcmenu.unpost()

Categories