Tkinter: Listbox separators, disabled items, keyboard navigation? - python

I'm studying the Tkinter Listbox widget and have been unable to find solutions for the following functionality:
How can I create non-selectable horizontal separator items, eg. separators equivalent to the Tkinter Menu widget's .add_separator()? (Using chars like dashes and underscores looks awful).
How can I disable a specific item? I tried using .itemconfig( index, state='disabled' ) without success.
How can I enable keyboard navigation, eg. when a user's keyboard input automatically scrolls one forward to the closest item that begins with the text the user typed? Must I bind(<KeyPress>, ...) and manage this behavior myself?
Would some of the above functionality be easier to implement using a Text widget or the ttk.Treeview widget?

you cannot. The widget doesn't support that.
you can't disable certain items, the widget doesn't support a state attribute. That being said, you can monitor the selection and do the appropriate thing if the user selects something that is disabled, and use the item foreground to denote disabled-ness.
You will need to bind to keypress events and manage the behavior yourself. It's not particularly difficult, just a little tedious.
the text widget might be your best bet, though you'll have to add bindings to mimic the default bindings of the listbox.
Bottom line: Tkinter provides nothing that directly supports what you want to do, but the building blocks are all there. You'll just have to build it yourself.

Related

Is there any Undo feature for changes in tags or appearance of text in Text widget tkinter?

.edit_undo() function is used for reversing the insertion or deletion of text in Text widget tkinter.
Is there any function that I can use to undo the setting of tags onto a certain range of text?
No, there is nothing built-in to handle that. The tk documentation explicitly states that the undo mechanism only applies to inserts and deletes:
The text widget has an unlimited undo and redo mechanism (when the -undo widget option is true) which records every insert and delete action on a stack.
There is no mention at all about anything else being handled by the undo/redo mechanism. If you have the need to undo/redo the application of tags, you will have to write the code to manage that yourself.

How can I create a read-only text widget with TkInter and still be able to modify the widget's content programmatically?

I want to use Tk's text widget to display part of the state of my program. (A label widget will not do, because the tag feature of the text widget will save a lot of work. A canvas widget will not do, because I don't want to have to lay out a lot of text manually.)
I do not want the user to be able to directly modify the contents of the text widget. They can change the state of the program by interacting with it in other ways, but the text widget is for display only.
If I set the state of the text widget to disabled, then not only is the user unable to interact with it, but I also cannot modify its contents programmatically (specifically, I cannot insert text).
The obvious workaround is decorate any code that updates the contents of the text widget with code the enables and disables the widget. But this is kludgy: I should be able to modify the contents without offering the user an opportunity to interfere, however brief that opportunity may be.
Is there a way to do this?
The "kludgy workaround" isn't kludgy at all -- that's exactly how you do it, and how it was designed to work.
However, there is another solution. You can remove all of the built-in key and mouse bindings from the widget, and then re-implement only the ones you care about (for example, you might want the user to highlight and copy a block of text). This is simple and effective, but if you want to restore some of the bindings it starts to become very tedious to re-implement the bindings you care about (cut, copy, paste, page up, page down, moving the cursor, etc).
To remove all of the default bindings you can remove the class bind tag like this:
t.bindtags((t, root, "all"))
For more information about bind tags, see these answers:
https://stackoverflow.com/a/11542200/7432
https://stackoverflow.com/a/3513906/7432
https://stackoverflow.com/a/11459001/7432
you could constantly delete then insert the text like so:
import tkinter
root = tkinter.Tk()
text = tkinter.Text(root, width=40)
text.grid(row=0, column=0, stciky=tkinter.W)
while True:
text.delete(0.0, tkinter.END)
text.insert(tkinter.END, your_text)
where your_text is the text you want to insert

GUI: Vertical list of arbitrary elements with TkInter

Sorry if complete basic question. I'm doing my first steps with TkInter, trying to develop a GUI interface to a video library (I do have experience though with WPF/XAML, the MVVM paradigm, and to some extent with html/css).
I would like to have a horizontal list, scroll-able, of custom elements. Each element will have video data, including metadata (length, bitrate, etc.), a picture (thumbnail), and some action buttons (play, delete, mark, etc.). Ideally I also want to have dragging handle, to rearrange the order of the videos.
I'm looking, but cannot find, what should be the equivalent of element in WPF, with binding to data and templates. Is that even exist in Tk, or is it too ambitious requirement for a portable, free framework?
Even if templates don't exist in Tk, I still can manage by populating the parent control manually (e.g. similar to HTML's elements - each has many controls) - but I couldn't event find a list of arbitrary widgets.
Is two way binding exists (e.g. adding an item to the list of items, should automatically update the UI)?
Is Tk the right direction? The underlying logic is in python, so I'm looking for a python friendly solution. Iron Python with WPF would be the obvious solution, but I want to be portable, and Iron Python's current state isn't very promising, and I'm using libraries not supported by Iron Python.
The most common way to make a custom element is to subclass Frame. You can put whatever you want in that frame (image, buttons, etc), and then you can treat the whole thing as a single GUI element.
import Tkinter as tk
...
class CustomElement(tk.Frame):
def __init__(self, parent, meta, image_file, ...):
tk.Frame.__init__(self, parent)
# <create widgets for image, metadata, etc here>
You can then create a custom widget that acts as a container for these items. For example, you can use a canvas which makes it easy to line this objects up horizontally, and to which you can attach a scrollbar.
Finally, tkinter has a robust event management system that makes it possible to move things around with drag and drop. There isn't any built-in drag and drop per se, but you can create your own grab handles and custom bindings for click and release to implement it.
For an example of moving things on a canvas with the mouse, see this answer: https://stackoverflow.com/a/6789351/7432

Automatic focus on showing deletes the placeholder text of a QLineEdit

I use PyQt4 and Python 2.7.9.
My program contains a few QLineEdit objects. The problem is that when the program is launched, one of the QLineEdits is being focused automatically, which causes my placeholder text to disappear.
Is there any way to prevent it, or at least don't let it hide the placeholder text?
Another way is
self.this_widget.clearFocus()
after window has been shown. Only in Qt5 placeholder texts are displayed even with focus. So maybe switch to PyQt5.
You can use setFocus to put the focus on a different widget (although, depending on which widget you pick, you might also need to set the focus-policy first):
self.some_other_widget.setFocusPolicy(QtCore.Qt.TabFocus)
self.some_other_widget.setFocus()
Alternatively, if you use Qt Designer to create the GUI, you could edit the tab-order so that the line-edit is not the first in the chain. This can also be done programmatically using QWidget.setTabOrder.

On-value-change type of event for widgets or use .trace_variable() technique?

Is there an on-value-change type of event for data input widgets like Entry, Text, Spinner, Checkbutton, Radiobutton? By on-value-change, I mean the ability to detect when the value of a widget has changed due to keyboard input or cut/delete/paste (and Text edit_undo/edit_redo) activity? I see no such event described in the Tkinter event documentation [1].
Is the proper technique to link Tkinter variables to widget values I want to monitor and use these variables' .trace_variable( 'w', ... ) methods to bind to value changes? This seems like the right approach, but I haven't seen a lot of trace_variable() use in the Tkinter application source code that I've studied ... leading me to be cautious about using this approach.
[1] http://infohost.nmt.edu/tcc/help/pubs/tkinter/events.html
Different widgets call for different solutions. For example, check buttons and radio buttons have a command option, and with an entry widget you can use the built-in validation features.
For all the widgets that can be tied to a variable, doing a variable trace is a common solution. The text widget is one exception since you can't associate it with a variable without a lot of effort.
In the tcl/tk world I associate all my widgets to a single array (tcl's name for a hash map / dictionary) and then put a single trace on the array. Unfortunately tkinter doesn't directly support tcl arrays. However, support is somewhat easy to hack in. For more information see my response to this question: How to run a code whenever a Tkinter widget value changes?

Categories

Resources