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.
Related
The QToolBar method insertWidget(QAction, QWidget) does correctly insert the widget at the intended place, but QToolBar.children() (actually QObject.children()) shows the output list of children ordered according to the order in which they were added or inserted, not in the order of their left-to-right appearance; the children() doc says that explicitly. That's probably not unreasonable, since children() is a method of QObject, and QObject knows nothing about its derived toolbar.
This may be related to a perplexing bug (IMO) in which QToolBar returns incorrect action objects on a mouse press event after an insertWidget(). I believe the system is counting the position of the clicked widget and returning its action object based on the (false) assumption that all widgets were added with addWidget(). But rather than dwell on this possible bug, my question is: is there a way to shuffle the output of children() such that it reflects the appearance of the widgets in the toolbar, rather than the order in which they were added or inserted? Brute force solutions are always possible, but I'd prefer something pythonic.
Edit:
(I think there used to be a link on this site, "answer your own question". I can't find it, so I'll post it here.)
It is not possible to shuffle the output of children() or its variants. However, a workaround is possible. The reason for wanting the toolbar items in order of appearance is so that they may be saved (e.g., in a ConfigParser file) and restored in the same order.
If any toolbar items have been added with insertWidget(), QToolbar.children() or its variants will not reflect the order of their appearance in the toolbar. IOW, code that looks like this is useless in finding widgets in toolbar order:
fieldlst = self.toolbar.findChildren(QPushButton,'', Qt.FindChildOption.FindDirectChildrenOnly)
However, a toolbar contains a layout which is implicity added and can be retrieved with QToolbar.layout(). Items can then be retrieved in the order of their appearance with code like this:
lt = self.toolbar.layout()
fieldlst = [lt.itemAt(k).widget() for k in range(lt.count())]
Cruder solutions are possible without using the layout, such as reading the enclosing rectangle of widgets returned by a children() variant, and then sorting on the coordinate of the left edge, but the solution given above seems simplest.
I have a simple QSplitter with a few widgets thrown in:
...
actions_splitter = QSplitter(
Qt.Horizontal,
frameShape=QFrame.StyledPanel,
frameShadow=QFrame.Plain
)
actions_splitter.addWidget(self.systems)
actions_splitter.addWidget(self.events)
actions_splitter.addWidget(self.compass_widget)
actions_splitter.setChildrenCollapsible(False)
...
The resulting splitters function perfectly fine, in that I can hover my mouse over their position, see the cursor change, and drag them around and change the sizes of the neighboring widgets.
The problem is that the handles themselves, the QSplitterHandles, absolutely refuse to have any visual modifications stick to them.
I have tried style sheets:
handle = actions_splitter.handle(0)
handle.setStyleSheet("{background-color: red}")
I have tried adding layouts with widgets to them:
handle = actions_splitter.handle(0)
layout = QHBoxLayout()
widget = QPushButton("HANDLE")
layout.addWidget(widget)
handle.setLayout(layout)
And absolutely nothing is doing anything to change how these handles appear. The only "visual" thing I can change is their width, and that's via QSplitter.setHandleWidth().
Someone please point out the painfully, stupidly obvious thing I'm missing here so I can get on with my life.
Premise
QSplitterHandle is one of the few very specific widgets that don't seem to follow generic QWidget "rules"; the reason is pretty simple: its aim is normally limited to the usage it's intended for, which is being a QSplitter child.
Then, you have to consider that, while pretty "liberal", the stylesheet syntax follows basic but important rules, it depends on the underlying QStyle in use and its behavior might change whenever a child widget is very dependant on its parent, as in this case.
First of all, there's a conceptual problem in your approach: the stylesheet syntax is wrong, as you're trying to use a selector-like syntax (using {braces}) without specifying a selector. With a syntax like that, the stylesheet won't be applied anyway, even with much more "standard" widgets.
But that's not enough. The QSplitter handle() documentation explains an important point (emphasis mine):
Returns the handle to the left of (or above) the item in the splitter's layout at the given index, or nullptr if there is no such item. The handle at index 0 is always hidden.
Even if your syntax were correct, it wouldn't have worked anyway since you tried to apply the stylesheet to the first (hidden) handle.
So, theoretically, the correct syntax should've been the following:
self.splitter.handle(1).setStyleSheet("background-color: red;")
even better:
self.splitter.handle(1).setStyleSheet(
"QSplitterHandle {background-color: red;}")
or, for all handles:
self.splitter.setStyleSheet(
"QSplitterHandle {background-color: red;}")
On the other hand, since QSplitterHandle is a very peculiar widget, its color properties are applied in different ways according to the currently applied QStyle, even when a correct syntax is used. Remember that stylesheets are overrides to the current QStyle, which means that the QStyles states the painting rules, and applies the eventual stylesheet according to those rules.
For instance, while the "windows" style uses the background color rule of the stylesheet to draw the actual background of the handle, the fusion style uses the background color to draw the "dots" of the splitter only.
As with other complex widgets (like QComboBox or QAbstractItemView), you need to use specific selectors for sub-controls, as clearly explained in the QSplitter stylesheet documentation; using the selector you ensure that the color rule is applied to the handle as a whole, no matter the style in use:
self.splitter.setStyleSheet("QSplitter::handle {background-color: red}")
I'm using Gtk to build an application on Linux using Python 3. I'm trying to use a Gtk.HeaderBar. So far it's been working Ok, but it seems that I can't get it to expand it's child widgets. For example:
As you can see above, I've tried putting my Gtk.Entry into the Gtk.HeaderBar, but even with things like Gtk.Entry.set_hexpand(True) it simply refuses to expand. I've even tried putting it inside a Gtk.Box, expanding the Gtk.Box, then adding the Gtk.Entry inside that. Even when I set the Gtk.Entry as a custom title for the Gtk.HeaderBar, this happens:
What's causing this? How can I fix it?
Enabling hexpand only says that you want the widget to be allocated all the remaining space; it does not actually resize your widget. You want the halign property set to GTK_ALIGN_FILL (or whatever it's called in Python) in addition to hexpand.
Check the diagrams on this page for a visual explanation.
You can use Box instead of Headerbar with window.set_titlebar method
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.
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.