Changing the selected item colour in a GtkTreeview using python - python

I have a dialog which contains a pygtk.treeview for listing tasks by priority. Each row has the background colour set based on that priority, so for example the highest priority has a light red background.
The row selection color is not so easy to change. I can set it using treeview.modify_base(gtk.STATE_SELECTED, "#C4C4C4"), but no colours work well with the colours used to enhance the concept of priority.
I had the idea to change the selection colour to be a slightly darker version of the colour used as the normal row background, so in the example above, that would be a darker red. I tried calling the function above in response to the treeselection's changed signal, and it works, but with heavy flickering.
Another idea was to change the selection to transparent and put a border around it instead, but as far as I can tell, this isn't possible.
How can I change the selection colour in the way described above without the flickering?
Can I change show the selection by having only a border around the row?
Note: I'm aware that this violates the theme selected by the user. I feel I have a good reason for it. Having the priority indicated by colour makes it instantly recognisable. The selection colour hides this. If you have alternative suggestions, I am open to them, but it needs to retain the ease at which a user can identify the priority.

You can use the method described here – I have tested it briefly and it does the job without flickering. Basically, the trick is to use the "Markup" property of the cell renderer. There's one catch, though: if you want to change the background colour with this method, only the background behind the "actual" text is changed, not the whole row. However, if you want to change the text colour (with <span foreground= ... ), which was actually
my intention, it looks ok.
I have the following CellDataFunc (this is C#, but I hope it's still useful):
private void CellDataFunc(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) {
Item item = (Item) model.GetValue (iter, 0);
if(cell is CellRendererText) {
int id = (int)column.GetData("colId");
string text = "";
switch(id) {
case 0: text = item.Name; break;
case 1: text = item.Size; break;
case 2: text = item.Time.ToString(); break;
}
//(cell as Gtk.CellRendererText).Text = text;
if(item.Highlight) {
(cell as Gtk.CellRendererText).Markup =
"<span background=\"red\">"+text+"</span>";
} else {
(cell as Gtk.CellRendererText).Markup = text;
}
}
}

You could add a separate pixbuf cell (say, the same size as a small icon) to the far left to indicate selection. Selected rows could fill this with a more "solid" (saturated) version of the colour used for the background. For example. if you use a pink background for high priority, you could use red for the selection indicator. Or you could use an icon.
To implement this with the colour filling method:
Disable the built-in highlighting as per Tobias' suggestion ("make the STATE_SELECTED color identical to STATE_NORMAL").
Create a widget based on gtk.gdk.Pixbuf that allows you to create a solid area of colour, perhaps using the fill method.
Use a CellRendererPixbuf for your "selection" cell.
You can then color or uncolour the "selection cell" upon selection changes to indicate which row is selected, or display an icon (eg. a stock symbol).
Note that I haven't implemented this, it's just an idea. It departs significantly from the usual GTK selection indication, so (obviously) use your judgement as to whether it's useable.

Not sure what you mean by flickering. A border would require subclassing TreeView.
I'd make the STATE_SELECTED color identical to STATE_NORMAL to disable the built-in highlighting. Then set a data_func on each column and change some color on the cell renderer depending on whether it's in the selection or not.
You're probably already doing this for your priority, so just multiply the color with something to highlight a row.

The question is outdated, but it is possible for someone else to be useful...
For totally disabling selection and making hover effect in the tree:
1. Disable system selection in the tree:
_treeView.Selection.Mode = SelectionMode.None;
Handle MotionNotifyEvent event:
[ConnectBefore]
private void TreeViewOnMotionNotifyEvent(object o, MotionNotifyEventArgs args)
{
TreePath treePath;
TreeIter iter;
int x = Convert.ToInt32(args.Event.X);
int y = Convert.ToInt32(args.Event.Y);
_treeView.GetPathAtPos(x, y, out treePath);
_treeView.Model.GetIter(out iter, treePath);
BindObject fieldModel = CellUtil.GetModelValue(_treeView.Model, iter, 1) as BindObject;
HoverLine = _vievModel.BindObjectCollection.IndexOf(fieldModel);
}
In the SetCellDataFunc method:
BindObject fieldModel = CellUtil.GetModelValue(treeModel, iter, 1) as BindObject;
int rowCount = _vievModel.BindObjectCollection.IndexOf(fieldModel);
if (HoverLine == rowCount)
{
cellRenderer.CellBackgroundGdk = ThemeUtility.GdkOddSelectionColor; //Your selection color
}
else
{
cellRenderer.CellBackgroundGdk = ThemeUtility.SystemSelectionColor; //Your system selection color
}
...

Calling both ModifyBase and ModifyText worked for me. Calling just ModifyBase changes the text color to White
Example in GTK C#:
treeViewList.ModifyBase(StateType.Selected, treeViewList.Style.Base(StateType.Normal));
treeViewList.ModifyText(StateType.Selected, treeViewList.Style.Text(StateType.Normal));

Related

Auto resize QLabel to fit updated QTableWidget span

I have a QTableWidget, and I have a QLabel that I put inside a cell of the table using .setCellWidget().
During run-time I change the span of the QTableWidget cell where the QLabel is, using .setSpan()
BUT when I change the span of the cell where the QLabel is, the QLabel does not resize.
Some code and screenshot below:
def generate_table(self):
global gtable
table = QTableWidget(20, 60)
gtable = table
def create_task(self):
task_widget = QWidget()
task_layout = QHBoxLayout()
task_widget.setLayout(task_layout)
task = QLabelClickable(the_task_name)
task_layout.addWidget(task)
gtable.setCellWidget(selected_row_column[0], selected_row_column[1], task_widget)
// if I include this part of the code, everything looks fine, both cell, widget and label scale properly, as visible through background color, below line is not the problem, notice its in the same function as where I set the cell widget
the_duration = 3
gtable.setSpan(selected_row, selected_column, 1, the_duration)
// Below is how I change the cell span. The rrow and ccolumn are integers, basically just cell coordinates
def save_task(self):
gtable.setSpan(rrow, ccolumn, 1, w_dr.value())
(there's a ton of code all over the place so I included what I thought is relevant, let me know what other parts of the code I should include)
This is what it should look like: (this is what the first span-changing line does)
This is what it looks like: (this is what the last line does)
My question is, How do I resize the QLabel / QWidget to auto fit the cell's updated size?
You can see the first span as correct because it's applied in the same event loop that would update the geometries of the view.
Spanning does not automatically do that (which could be a bug), so the solution is to use updateGeometries(), which:
Updates the geometry of the child widgets of the view.
Which means that all widgets of the view will be correctly resized and updated, including scroll bars and cell widgets.
def save_task(self):
gtable.setSpan(rrow, ccolumn, 1, w_dr.value())
gtable.updateGeometries()
A very important suggestion: avoid using globals, they are not handy as one would think, and in fact the often cause problems and bugs that are difficult to track; use instance members instead (eg. self.gtable).

How to dissable the focus indication in stylesheet

When my QDoubleSpinBox is focused, it gets a blue outline to it (in "Fusion" style):
How do I turn this off?
Doing this with stylesheets only is doable, but has an important drawback: styling complex widgets like a QSpinBox requires to correctly set all sub control properties.
The basic solution is to set the border for the widget:
QSpinBox {
border: 1px inset palette(mid);
border-radius: 2px;
}
Keep in mind that offering proper visible response of the focus is really important; you might not like the "glow" (and color) the Fusion style offers, but nonetheless it should always be visible when a widget has focus or not, even if it has a blinking text cursor. You can do that by specifying a slightly different color with the :focus selector:
QSpinBox:focus {
border: 1px inset palette(dark);
}
Unfortunately, as explained in the beginning, this has an important drawback: as soon as the stylesheet is applied, the widget painting falls back to the basic primitive methods (the spinbox on the right uses the stylesheet above):
Unfortunately, there's almost no direct way to restore the default painting of the arrows, as using the stylesheet prevents that. So, the only solution is to provide the properties for the controls as explained in the examples about customizing QSpinBox.
There is an alternative, though, using QProxyStyle. The trick is to intercept the control in the drawComplexControl() implementation and remove the State_HasFocus flag of the option before calling the default implementation.
In the following example, I also checked the focus before removing the flag in order to provide sufficient visual feedback, and I also removed the State_MouseOver flag which shows the glowing effect when hovering.
class Proxy(QtWidgets.QProxyStyle):
def drawComplexControl(self, cc, opt, qp, widget=None):
if cc == self.CC_SpinBox:
opt = QtWidgets.QStyleOptionSpinBox(opt)
if opt.state & self.State_HasFocus:
opt.palette.setColor(QtGui.QPalette.Window,
opt.palette.color(QtGui.QPalette.Window).darker(100))
else:
opt.palette.setColor(QtGui.QPalette.Window,
opt.palette.color(QtGui.QPalette.Window).lighter(125))
opt.state &= ~(self.State_HasFocus | self.State_MouseOver)
super().drawComplexControl(cc, opt, qp, widget)
# ...
app = QtWidgets.QApplication(sys.argv)
app.setStyle(Proxy())
# ...
Note that the above "color correction" only works for Fusion style and other styles that use the Window palette role for painting the border. For instance, the Windows style doesn't consider it at all, or you might want to use higher values of darker() or lighter() in order to provide better differentiation.

How to control the distance between a Gtk.Frame and Gtk.HBox inside?

Consider this example:
You can see in this window, is a Gtk.Frame, named "Coefficients". Inside of that is a Gtk.HBox. Inside the Gtk.HBox, are ten Gtk.Scales.
What I'd like to do, is control the padding between the Gtk.Frame, and anything that's inside, effectively the Gtk.HBox, or rather, the Gtk.Scales inside of that. How would I go about that?
In case this is important, I'm using Python3 and Gtk3.
I seem to have made progress going about it a quite different way, however there seem to be inconsistencies.
I defined a CSS with the following contents (self is a Gtk.Window):
self._css = b"""
#coeff_container {
padding: 20pt;
}
"""
The Window this is displayed in is composed like this:
Gtk.Window > Gtk.Frame > Gtk.HBox > ten Gtk.Scales
self._hbox = Gtk.HBox()
The Gtk.HBox has the property "name" set like this:
self._hbox.set_name("coeff_container")
self._hbox.set_spacing(20)
Now, as you can see, I've set the spacing to 20, but the padding property is set in the CSS.
Applying the CSS is done as follows:
self._style_provider = Gtk.CssProvider()
self._style_provider.load_from_data(self._css)
Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(),
self._style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATIONS )
I cannot set the spacing property in CSS, it only works with .set_spacing(int), on the other hand, I can't set padding not in CSS, it is quite confusing like that.

disable glow effect of gtkbutton

I would like to know that how to disable the glow effect of gtkButton on mouseover.
Please help me.
Thank you in advance.
This may be what you are looking for. It is c++, but hopefully you can translate it to the python equivalent. The code changes the background color of a widget in different states.
void changeColor(Gtk::Widget* widget, double r, double g, double b) {
Glib::RefPtr<Gdk::Colormap> colormap = widget->get_colormap();
Glib::RefPtr<Gtk::Style> style = widget->get_style()->copy();
// STATE_NORMAL (most of the time)
{
Gdk::Color color;
color.set_rgb_p(r,g,b); // <-- customize this
colormap->alloc_color(color);
style->set_bg(Gtk::STATE_NORMAL, color);
}
// STATE_PRELIGHT (when mouse hovers)
{
Gdk::Color color;
color.set_rgb_p(r*0.9,g*0.9,b*0.9); // <-- customize this
colormap->alloc_color(color);
style->set_bg(Gtk::STATE_PRELIGHT, color);
}
// STATE_ACTIVE (when clicked)
{
Gdk::Color color;
color.set_rgb_p(r*0.8,g*0.8,b*0.8); // <-- customize this
colormap->alloc_color(color);
style->set_bg(Gtk::STATE_ACTIVE, color);
}
widget->set_style(style);
}
You are interested in setting the right colors in the STATE_PRELIGHT block so that they are the same than in the STATE_NORMAL block. (I'm guessing this is what you mean with your question).
By the way, there are two other states which are not handled in the method above: STATE_SELECTED and STATE_INSENSITIVE. It must also be stated that this function, despite its name, doesn´t actually change the color of any widget. It won't change the color of a label, for example, since a label takes the color of it's container. So don't take the function signature too seriously.
The following Python code will make a copy of the button's style, set the background color in the gtk.STATE_PRELIGHT (mouseover) state to the same color as gtk.STATE_NORMAL, and then set the style back on the widget.
import gtk
window = gtk.Dialog()
button = gtk.Button('Click Me')
style = button.get_style().copy()
style.bg[gtk.STATE_PRELIGHT] = style.bg[gtk.STATE_NORMAL]
button.set_style(style)
window.vbox.pack_start(button)
window.show_all()
window.run()
Since this doesn't mess with the actual state of the button, there are no side effects to the rest of your code.
This code MAY have the an affect on GDK themes. If I discover a theme-safe method for this I will edit my answer.
import gtk
def enter(widget, event):
widget.set_state(gtk.STATE_NORMAL)
return False
win = gtk.Dialog()
button = gtk.Button('Button')
button.connect_after('enter-notify-event', enter)
win.vbox.pack_start(button, 0, 0)
win.vbox.show_all()
win.run()

how to change the color of a QGraphicsTextItem

i have a scene with a multiple (QGraphicsTextItem)s, and i need to have control over their colors , so how to change a color of a QGraphicsTextItem ? is it possible anyway? i've been trying for 3 days until now . please help
thanks in advance
I think you can change the text color by calling the method:
void QGraphicsTextItem::setDefaultTextColor ( const QColor & col );
You have an example here.
Or looking for Diagram Scene Example in your Qt Assistant.
setDefaultTextColor(col) "Sets the color for unformatted text to col." The documentation is not clear about what "unformatted text" means. I think it means: "all portions of the contents of the item that have not been styled."
The contents is a QTextDocument.
You style a part of a document using a QTextCursor. You can't style the QTextDocument per se, only a part that is selected by a QTextCursor (but you can select the whole document.)
You can style a QTextCursor using method mergeCharFormat(QTextCharFormat)
The QTextCharFormat has methods:
foreground().setColor(QColor)
setForeground(QBrush)
setTextOutline(QPen)
Foreground is a QBrush that paints several things including "text" (but better said: the fill of characters?)
One nuance is that certain newly constructed QBrush have (default to) QBrushStyle.NoBrush, which is transparent, even if you setColor().

Categories

Resources