disable glow effect of gtkbutton - python

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()

Related

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.

Qt FlowLayout example - how to get sizeHint to be called when layout changes?

I want to arrange a bunch of QPushButtons in a layout so they will wrap horizontally (like text). I'm trying to use the Qt example FlowLayout in PySide2.
I've posted a simple example revision 2 here
(This is based on the official example here, which is why I'm so perturbed it doesn't work well.)
Specifically, it works well when the window is large enough:
but doesn't prevent the window from being shrunk down small, whereas a VBoxLayout or GridLayout would:
I think the minimum size should depend on the current width. This should be a minimum size:
and so should this:
How can I get the layout to prevent its parent from being shrunk so small that its contents aren't visible?
sizeHint() and minimumSize() always return the size of a single item, which I thought was the problem. However if I rewrite them to take into account all items at the current layout width, it doesn't matter, because both are only called once, when I create and populate the layout.
My best idea so far is to call update() from heightForWidth(), roughly this:
def heightForWidth(self, width):
oldWidth = self.geometry().width() - 2 * self.contentsMargins().top()
oldHeight = self.doLayout(QtCore.QRect(0, 0, oldWidth, 0), True)
height = self.doLayout(QtCore.QRect(0, 0, width, 0), True)
print 'heightForWidth', width, oldWidth, height, oldHeight
if height != oldHeight:
self.update()
return height
full sample with changes to minimumSize and doLayout is revision 3.
It works sometimes, but has several problems:
When the layout is first created there are constant calls to update(). I think the call to update() triggers a call to heightForWidth() and for some reason the width passed in is not close to the "oldWidth" that I calculate until I resize the window, even by a single pixel.
As soon as I resize the window, calls to heightForWidth() stop completely, and minimumSize() stops being called, and I can no longer resize my window smaller than the last size calculated. I've no idea why.
(Much less important) self.geometry().width() - 2 * self.contentsMargins().top() does not return the width that was last passed to heightForWidth (even when top/bottom/left/right margins are identical), though I took it from other code in the example. This doesn't usually matter but at certain widths it causes a flurry of calls to update()
I suppose I'm doing it all wrong. Can it be done right?
You can modify just the setGeometry() and minimumSize() functions. I worked in C++, but it is easily modified to Python.
First, I changed the QSize returned by minimumSize() to take into account the current size of the layout and subsequently set the height for the returned QSize. Then, I call update() in setGeometry() to force the layout to reexamine the sizeHint(). Below is an example of the code.
QSize FlowLayout::minimumSize() const
{
QSize size;
for (const QLayoutItem *item : qAsConst(itemList))
{
size = size.expandedTo(item->minimumSize());
}
const QMargins margins = contentsMargins();
size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
// ADDED CODE **************************************
auto layoutSize = this->contentsRect();
size.setHeight(heightForWidth(layoutSize.width()));
// *************************************************
return size;
}
void FlowLayout::setGeometry(const QRect &rect)
{
// ADDED CODE ************************************
update();
// ***********************************************
QLayout::setGeometry(rect);
doLayout(rect, false);
}
Hope this helps.
I've ported the original/official example to PySide2 now, and it works without changes. I'm not sure what was preventing it from working before.

Why css style don't work on GtkButton?

Please excuse my english.
I'm trying to change the background color of a GtkButton using a css file but I can't.
I tried a few examples I found on the web, but none work.
I post two examples. One in Python 3.2.3 and the other in C
I'm using Gtk+ 3.6 and Kubuntu 12.10.
This is the code of one of them:
from gi.repository import Gtk, Gdk
class MainWindow(Gtk.Window):
def __init__(self):
super().__init__()
vbox = Gtk.Box(spacing=10,orientation=Gtk.Orientation.VERTICAL)
self.add(vbox)
self.entries = [ Gtk.Entry() for i in range(3) ]
for e in self.entries:
vbox.pack_start(e, True, True, 0)
e.connect("changed", self.on_entry_changed)
e.set_text('123')
button=Gtk.Button(label='ok')
vbox.pack_end(button,True,True,0)
def on_entry_changed(self,entry):
ctx = entry.get_style_context()
if not entry.get_text().isnumeric():
ctx.add_class('invalid')
else:
ctx.remove_class('invalid')
cssProvider = Gtk.CssProvider()
cssProvider.load_from_path('style.css')
screen = Gdk.Screen.get_default()
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(screen, cssProvider,
Gtk.STYLE_PROVIDER_PRIORITY_USER) # With the others GTK_STYLE_PROVIDER_PRIORITY values get the same result.
window = MainWindow()
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()
and the style.css
GtkEntry.invalid {
background-color: #ffaaaa;
background: #ffaaaa;
}
GtkButton {
engine: oxygen-gtk; /*tried also with 'none' and without setting engine*/
background-color: green;
background: green;
}
The entries works well... the bg color change. But the Button no, and theres no error messages.
EDIT3: (Deleted previews edits and change some tags)
Summarizing... I tried to change the button color with all the Python, C, and C++ codes I found in the web unsuccessfully. I read all the tutorials I found and the GTK+ 3 reference manual.
All that I know after that is that the problem is about Kubuntu themes: If I change the GTK theme from 'oxygen-gtk' to 'default' (in GTK Configuration), is the only way I found that the code works well, but this is not the idea and the button looks horrible.
So, the questions are:
Why I can't change the background color of the button?
Why I having this problem only with buttons? (Works well with other widgets)
I get answers here and in GTK forums saying that is not a good practice to change button colors, but... What if I want a menu like the one in this image (link) (see red box buttons)? Wich is the best practis for that?
Thanks and greetings!
I know this is quite old, but popped up in the first few google results, so I thought I'd share my experience.
Gtk.Button has an inline Gtk.Label for the button text, that doesn't inherit from the button by default, so you have to explicitly tell it to (or just specify the colour in it):
GtkButton GtkLabel {
color: #fff; /* This changes the text color in the button */
}
As far as the answer from #sciamp, the GTK theme sets an image for the background and the borders as well, so you have to manually remove that with background-image: none; border-image: none; Hope this saves someone the struggle.
This should work (I mean it's working for me!):
GtkButton {
border-image: none;
background-image: none;
background-color: green;
}
This is complicated, but I don't think it can be done, directly.
I believe the core reason is because the button doesn't render the background. All it does is rendera frame around its area, and then render any children inside. Remember that GtkButton is a container, it typically holds a GtkLabel for a textual label but can hold any widgetry.
I've managed to change the background color of textual labels, but then only the much tigher box around the text itself is affected, which is not what you want.
The indirect solution is to subclass the GtkButton to create a variant which actually does render its background. This is, to be sure, pretty rude towards themes and should be avoided.

how to create a transparent rectangle responding to click event in Tkinter

I need to draw a rectangle in a tkinter.canvas to respond click event:
click_area = self.canvas.create_rectangle(0,0,pa_width,pa_height,fill='LightBlue',outline='lightBlue',tags=['A','CLICK_AREA'])
self.canvas.tag_bind('CLICK_AREA','<Button>',self.onClickArea)
it works.
at this moment, I have to draw a series of grid on the canvas, and I want them to be covered with the click_area, so that I need to make the click_area transparent.
but, when I wrote like this:
click_area = self.canvas.create_rectangle(0,0,pa_width,pa_height,fill='',outline='lightBlue',tags=['A','CLICK_AREA'])
it did not respond to click any longer.
So, my question is how to make it transparent and keep it responding to click. Or, is there any other way to implement what I want.
Thank you very much.
I came across this same issue trying to use the find_closest Canvas method to modify existing rectangles, but simply binding to the canvas didn't work. The issue is that a Tkinter rectangle without a fill will only respond to clicks on its border.
Then I read about the stipple argument to create_rectangle from here:
stipple: A bitmap indicating how the interior of the rectangle will
be stippled.
Default is stipple="", which means a solid color. A
typical value would be stipple='gray25'. Has no effect unless the fill
has been set to some color. See Section 5.7, “Bitmaps”.
And the section on bitmaps states that only a few stipple options are available by default, but none of them are completely transparent. However, you can specify your own custom bitmap as an X bitmap image (a .xbm file).
XBM files are really just text files with a C-like syntax, so I made my own 2x2 bitmap with all transparent pixels and saved it as transparent.xbm in the same directory as my Tkinter script. Here's the code for the XBM file:
#define trans_width 2
#define trans_height 2
static unsigned char trans_bits[] = {
0x00, 0x00
};
Then, you can specify the custom stipple by prefixing the xbm file name with a # when creating your rectangle:
self.canvas.create_rectangle(
x1,
y1,
x2,
y2,
outline='green',
fill='gray', # still needed or stipple won't work
stipple='#transparent.xbm',
width=2
)
Note, you still need to provided some fill value or the stipple will not be applied. The actual fill value doesn't matter as the stipple will "override" it in the canvas.
I think I got it: Bind the canvas, not the rectangle.
replace
self.canvas.tag_bind('CLICK_AREA','<Button>',self.onClickArea)
with
self.canvas.bind('<Button>',self.onClickArea)
problem solved.

Changing the selected item colour in a GtkTreeview using 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));

Categories

Resources