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.
Related
I got a couple of questions regarding qDialogButtonBox. While my code still works, I believed that there are a few parts that can be better refined/ I am not finding much info online
class testDialog(QtGui.QDialog):
def __init_(self, parent=None):
...
self.init_ui()
self.signals_connection()
def init_ui(self):
...
self.buttonBox = QtGui.QDialogButtonBox()
self.buttonBox.addButton("Help", QtGui.QDialogButtonBox.HelpRole)
self.buttonBox.addButton("Apply", QtGui.QDialogButtonBox.AcceptRole)
self.buttonBox.addButton("Cancel", QtGui.QDialogButtonBox.RejectRole)
#
def signals_connection(self):
self.test_random.clicked.connect(self.test_rand)
# Is this the latest/correct way to write it?
self.buttonBox.accepted.connect(self.test_apply)
self.buttonBox.rejected.connect(self.test_cancel)
self.buttonBox.helpRequested.connect(self.test_help)
def test_apply(self):
print "I am clicking on Apply"
def test_cancel(self):
print "I am clicking on Cancel"
self.close()
def test_help(self):
print "I am clicking for Help!"
My questions are as follows:
Under my function - signals_connection(), the lines that I wrote for
the buttonBox (though the code still works) are quite different
for the signal I have wrote for the self.test_random and I am
unable to find any similar online for the qdialogbuttonbox.. There
is another style that I have found - self.connect(self.buttonBox,
QtCore.SIGNAL("accepted()"), self, QtCore.SLOT("accept()")) but I
think that is the old style?? Otherwise what should be the right way
to write it?
In my test_cancel() function, is writing self.close() the best
way to close the application? The way that I run my program is as
follows:
dialog = testDialog();dialog.show()
Lastly, is it possible to add 3 different tool tips to the 3 buttons I have created? I saw that there is a command for it - self.buttonBox.setToolTip("Buttons for life!"), but this will results in all 3 buttons to have the same tool tip. Can I make it as individual?
Yes, that is the correct way to write signal connections (the other syntax you found is indeed the old way of doing it). You can find all the signals in the pyqt documentation for QDialogButtonBox. Different widgets and objects have different signals. QPushButton's and QDialogButtonBox's have different signals.
Yes, close() will close the dialog. The QApplication will exit by default if there are no other windows open. However, if this is a modal dialog, you typically want to close a dialog with either the accept or reject command. This will alert the calling function as to whether the dialog was closed with the Ok/Yes/Apply button or closed with the No/Cancel button.
You can set different tooltips for different buttons in the QDialogButtonBox. You just need to get a reference to the specific button you want to set the tooltip for.
For example
self.buttonBox.button(QDialogButtonBox.Help).setToolTip('Help Tooltip')
self.buttonBox.button(QDialogButtonBox.Ok).setToolTip('Apply Tooltip')
Or you could loop through all the buttons
for button in self.buttonBox.buttons():
if button.text() == 'Help':
button.setToolTip('Help Tooltip')
elif button.text() == 'Apply':
button.setToolTip('Apply Tooltip')
Also, you could connect the accepted and rejected signals from the QDialogButtonBox to the accept and reject slots on the QDialog
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
That way, you won't have to manually connect the Ok and Cancel buttons to your callbacks for closing the dialog.
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;
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()
I am facing the problem to need tabs in a pygtk app. Pretty much just like gedit has, but without any per-child widget content.
I’ve come across gtk.Notebook, but that requires me to put a widget for each tab, which I don't want.
The reason is, that I have one widget, but would only like to updates its content based on which tab is selected.
Any hints on how to do that?
My idea so far would be to just add some invisible widget for each tab and then connect to the select-page signal. Which widget could I use as invisible widget, or is there a better/alternative way of achieving my goal?
The invisble widget idea works. But not with gtk.Invisible (this just crashes), but with gtk.HBox() or any other thing that seems empty.
self.notebook.append_page(gtk.HBox(), gtk.Label("title"))
Now if I want to display stuff inside the tab actually, I can use reparent to move the widget to the current tab like this.
class Tab(gtk.HBox):
def __init__(self, child):
self.child = child
self.notebook.append_page(Tab(myWidget), gtk.Label("title"))
def pageSelected(self, notebook, page, pagenum):
box = notebook.get_nth_page(pagenum)
box.child.reparent(box)
You can have global widgets, one per tab as you want, in order to access them easily when the tab is selected.
self.notebook.append_page(self.rightBox, gtk.Label("Orders"))
Then connect to the "switch page" signal
self.notebook.connect("switch-page", self.pageSelected)
and :
def pageSelected(self, notebook, page, pagenum):
name = notebook.get_tab_label(notebook.get_nth_page(pagenum))
Now you have "name" with the label of the currently selected page. Just test it (if name == "Orders" ...) to interact.
Hope this was of some help !
This is related to another question I found here that seems to be inactive for a few months, so I think it's worth asking again.
I have created a simple QDialog that has a QTextEdit and a QPushButton. This pops up in my application when a user right-clicks and selects the option to "add comments". I want them to be able to write free-form text and I'll just save whatever they write as a long string with no concern for new lines, etc.
When the user clicks the button, it executes code like this:
self.connect(accept_button,QtCore.SIGNAL('clicked()'),lambda arg=str(view_textedit.toPlainText()): self.updateGroupComments(arg))
def updateGroupComments(self,new_comment_str):
print "Updating user comment to have new string: " + new_comment_str
self.group_entry.list_of_user_comments[self.currentFrameCounter] = new_comment_str
This is not detecting the TextEdit text that is visible (it only detects whatever the text edit text is set to when it is created). How do I make a simple command that returns the currently visible text from a QTextEdit. Again, the function
toPlainText()
is not working correctly... it doesn't find the currently visible text, only whatever text was on screen before changes or additions started being made by the user.
If this can't be done without subclassing and appealing to cursor positions, it makes the whole thing seem worthless... so please keep suggestions only to those implemented without subclassing or manipulating cursors. It should be really simple and straightforward to just return all currently visible text... what am I missing?
Objects that are being bound to default arguments are evaluated at the definition time. The function is working correctly, it returns whatever was in the text field when it was executed. Your code simply calls it at the wrong moment. If you want to use lambda, then do:
self.connect(
accept_button, QtCore.SIGNAL('clicked()'),
lambda: self.updateGroupComments(str(view_textedit.toPlainText()))
)
Or make view_textedit an instance attribute instead, and do simply
self.connect(
accept_button, QtCore.SIGNAL('clicked()'), self.updateGroupComments
)
And change updateGroupComments to call self.view_textedit.toPlainText instead of taking an argument.
BTW, this is not PyQt specific, this is how Python works in general.
To illustrate my last comment, that lambda can very well be replaced with:
def slot():
self.updateGroupComments(str(view_textedit.toPlainText()))
self.connect(accept_button, QtCore.SIGNAL('clicked()'), slot)