So I currently have a ScrolledPanel that contains a number of TextCtrls that are placed in a vertical BoxSizer programmatically. The reason I'm doing this instead of just appending lines to one big scrolled TextCtrl is so that I can also add other controls in between the TextCtrl, such as images or stylized expand/contract folding stuff.
However, this particular implementation is causing a problem - namely that it is impossible for the user to select text across multiple TextCtrls. Is there a way to do this that will be fast, clean, idiomatic, and not especially kludgy? Is my best bet to write a pointer-location text selection algorithm that essentially reinvents the wheel for the text selection stuff of the underlying native libraries, or is there an easier way to embed other controls inside a multiline scrollable TextCtrl, or even select text across multiple TextCtrls natively?
I would stay away from trying to reimplement text selection controls if at all possible, since that is bound to turn very messy very fast. Another way you could tackle this issue would be to use a single multi-line textctrl widget with the other widgets tacked on over it. This is also messy, but less so.
You can place the other widgets over the textctrl simply by placing them directly over the same position as the textctrl, so long as the other widgets have the same parent as the textctrl. This should work, so long as you don't overlap with the vscrollbar (or, better yet, remove it entirely with style=wx.TE_NO_VSCROLLBAR).
The next thing you'll need to do is pre-fill and space your textctrl so that the user has control of text only right after the position of each widget. You should have each line of text with a different spacing setting, set with the spacing options of wx.TextAttr (the more generic versions of double-spacing, etc), which you calculate based on the particular widget spacing you've given your app. This is necessary to force the user to type only exactly where you want them to.
Next, you'll need to set up a binding to the textctrl newline character that recalculates the spacing needed for each line. Once you've figured out how to handle spacing, this shouldn't be too difficult.
Finally, after you select the text, just reset everything to the same spacing, or whatever else suits your fancy, so that you don't get awkward linebreaks when you paste it back in elsewhere.
I know this is a complicated answer, but it's a complicated issue you raised. This is, I believe, the most efficient way to solve it, and avoids all the bugs that would arise from completely overhauling the textctrl, but it does involve messing around with auto-correcting linebreaks and spacings, which can be a little tricky at first.
Related
.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.
I'm using Gtk.Fixed and a viewport putting images that, sometimes, overlaps. How can I set the z-index like in html?
Gtk.Fixed doesn't support setting the z-order. It's not meant for overlapping widgets, so the z-order is arbitrary and probably depends on what order the widgets were added in.
It's not clear from your question what your intention is; if you are purposely overlapping widgets, use Gtk.Overlay instead. If you are not intending to overlap them, then you should use a more versatile container such as Gtk.Grid which will take the widgets' sizes into account.
Like ptomato said, I had to use the Gtk.Overlay. I used an overlay with 3 Gtk.Layout, like layers and it works fine.
I only used the C bindings, but I'll try my best to hopefully answer it correctly for the Python bindings as well.
Generally, since GTK4 you can move around your widget using
widget.insert_before (sibling, parent)
Where you might want to obtain the sibling (which will be in front of your widget) and it's parent first.
The function expects the parent to be the parent of the sibling.
So it can be assumed that:
parent = sibling.get_parent ()
At least within a GtkFixed (but possibly other containers as well), siblings are sorted bottom to top, so the first child is the one that's the furthest to the back. "Inserting before" thereby moves your widget behind.
Be aware though, that according to a maintainer, this should only be used within a custom widget and not on an application scale. This is also mentioned in the API of the functions.
From the documentation however, it's not clear to me if this is considered legal. I doubt that this way is supported. However I also doubt, that GtkFixed keeps any track regarding the children order and therefore, I assume that moving children around within a GtkFixed is fine, although illegal.
Moving the widgets around like this also made my application crash, as long as the inspector is open, just as a heads up.
To iterate through the widgets, bottom to top, you can use sibling = parent.get_first_child() as well as sibling = parent.get_next_sibling()
or alternatively use the iterator defined on Widget.
For a Python3/PyQt4 project, I'm looking for the fastest way to change the text appearance of several words in a QTextEdit object. The text is not written in HTML (it's a pure text string), is read-only and is made of several words. Each word has a special 'attribute' defined in my code, defining its appearance when this word is hovered over.
The appearance of all words sharing the same attribute must change when one of these words is hovered over.
I need speed since :
I did more or less the same program but the the words' appearance changed when the user clicked on them and I guess my code is too slow to be used with hover events. (see details below)
It's a Python project based on PyQt4, not a C++ one based on Qt.
Any help would be appreciated !
MORE DETAILS :
I can see two ways to achieve my goal :
(1) Write my text in the editor, detect which word the mouse "flies over", get the other words to be highlighted and painfully select them, one by one, inserting some HTML code to modify their appearance. It's the painfully part that I find too complex and too slow : is there a fastest way to do this ?
(2) Writing my text as an HTML one and work with the CSS since a QTextEditor can use them. Alas, the easy way can't work. But perhaps is it possible to "re-load" the CSS of the text and use a more clever trick ?
I'm wondering is it even possible to get the word under mouse cursor from the QTextEdit.
You said the text cannot be edited, so I maybe the QTextEdit is not the best possible widget for this job. Create your own widget and draw the text by yourself. Then you know the word positions so finding the word under the mouse is easy. And when some attribute is activated or deactivated, you can redraw only the affected words.
How would i go about setting up a Tkinter Text widget to do something similar to IDLE's entry? For example:
>>> Entry goes here!
However, i know how to insert them at the beginning of each line, but how would i go about making it non deletable, so that you cannot delete the >>>? I have searched around on google about this, but to no avail.
If the solution only has to be "Good Enough", the technique I would use is this:
When you insert the prompt, remember the index of the end of the prompt.
Add a binding to the widget for the events you care about (eg: <BackSpace> and <Delete> and <<Cut>>)
In this binding you can look at the index of the insertion cursor and selection, and if it's prior to the saved index, ignore the event (ie: do a return "break").
This should work more-or-less OK, though it allows you to insert characters prior to the prompt. Rewriting all the bindings that alter a text widget is a fairly daunting task, but just tracking deletes isn't too hard.
To solve the problem perfectly would require you write a little tcl code to intercept the low level insert and delete commands of the actual widget. It's possible, though it requires a decent understanding of the underlying tcl code. For an example, see this answer: https://stackoverflow.com/a/11180132/7432
When I try to scroll down to the end of my TreeView, which is inside a ScrolledWindow, it doesn't scroll where it should but one or two lines before.
I tried several methods and they all provide the same behavior :
self.wTree.get_widget("tree_last_log").scroll_to_cell((self.number_results-1,))
# or
self.wTree.get_widget("tree_last_log").set_cursor((self.number_results-1,))
# or
adj = self.wTree.get_widget("scrolledwindow1").get_vadjustment()
adj.set_value(adj.get_property('upper'))
self.wTree.get_widget("scrolledwindow1").set_vadjustment(adj)
# or
self.wTree.get_widget("scrolledwindow1").emit('scroll-child', gtk.SCROLL_END, False)
Where is the problem ?
The C API docs may be helpful:
http://library.gnome.org/devel/gtk/stable/GtkTreeView.html#gtk-tree-view-scroll-to-cell
You can see there are arguments there that would mess things up, depending on how pygtk defaults them. You might try specifying explicitly all the args.
One trick to TreeView and TextView is that they do asynchronous layout so the "upper" on the adjustment may well just be zero if row heights haven't been computed yet.
if messing with the adjustment, there's no need to set it back, though it should be harmless.
'scroll-child' signal is not what you want, that's a keybinding signal used to bind keys to.