method to draw circular buttons in pyqt5 - pyqt

i would like to try to create a row of circular buttons with a method different with the one that uses setStyleSheets to define the color, the thickness and so on. The code that I managed to get with that method is the following:
self.button_list = []
for i in range(10):
button = QPushButton("{}".format(i+1), self)
button.setFixedSize(QSize(50, 50))
button.setStyleSheet("border : 1px solid black;background-color : green; color: yellow; border-radius : 25px")
self.button_list.append(button)
mainlayout.addWidget(button, i)
With what other method can I achieve the same results of circular buttons?

Related

How to set graphics for a push button using Qt Designer?

I created a pushButton with a border image using Qt Designer. The stylesheet of the pushButton looks like the following.
border-image: transparent;
border-image: url(:/button_img/main_button.png);
The button looks like as follows:
The button works as expected but I do not know how can I set border inset or onset for that button. My terminologies might be wrong because I am very new to GUI development. What I am looking for is when you creata a normal pushButton, you can visualy see when you click that button. But in my case the button works but I cannot see the graphics when I click the button.
How can I add the border inset or onset? Please let me know in case of additional information.
No, you can't; not like that.
As the name hints, the border-image CSS property is used to draw borders; while what you see seems like a "background image", it actually is a border object that splits your image in a 3x3 grid rectangle that covers the whole button area; each section is then "stretched" to fill its own rectangle.
+-----------+---------+------------+
| top left | top | top right |
+-----------+---------+------------+
| left | center | right |
+-----------+---------+------------+
|bottom left| bottom |bottom right|
+-----------+---------+------------+
To understand how all of this works, read the "Common Mistakes" section of the stylesheet examples documentation. Here's what actually happens (I'm using half-width/half-height margins for the sake of the argument, so only the 4 "angle" rectangles are shown, so the left, top, right, bottom and center rectangles will be null):
If you want to use style sheets that interactively show borders, the standard approach is have 2 different images for the button state, and both of them needs their border. If you also want to achieve the "hover" effect, you'll need a third image that will have no border at all.
The downside of this method is that if the icon is resized, the border will be resized too.
The only alternative I can think of is to subclass your own QPushButton widget and override its paintEvent method. You could also use an event filter, but that wouldn't give you any major benefit, as it would use a very similar approach anyway, while complicating things if the object tree is even slightly complex.
In the following example I use an implementation that is based on stylesheets only: Qt can use the qproperty-* stylesheet parameter to set existing properties of a class (Qt properties that are already part of the class, or that are added in the class constructor: using setProperty on an unexisting property within the __init__ method will not work).
While this kind of implementation is usually not required (you could set the pixmap attribute of the class instance by hand) the advantage is that with this approach you can set everything within the stylesheet only.
class PixmapButton(QtWidgets.QPushButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._pixmap = QtGui.QPixmap()
self.setCheckable(True)
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
#QtCore.pyqtProperty(QtGui.QPixmap)
def pixmap(self):
return self._pixmap
#pixmap.setter
def pixmap(self, pixmapPath):
self._pixmap = QtGui.QPixmap(pixmapPath)
def paintEvent(self, event):
opt = QtWidgets.QStyleOptionButton()
self.initStyleOption(opt)
qp = QtGui.QPainter(self)
# draw the basic button
self.style().drawControl(QtWidgets.QStyle.CE_PushButton, opt, qp, self)
if self._pixmap.isNull():
return
# get the minimum possible size for the pixmap icon
minSize = min(self.width(), self.height())
# remove the margin/padding usually necessary for the button contents
minSize -= self.style().pixelMetric(QtWidgets.QStyle.PM_ButtonMargin, opt, self) * 2
# create a rectangle based on the minimal size and move it to the center
# of the button
rect = QtCore.QRect(0, 0, minSize, minSize)
rect.moveCenter(self.rect().center())
# finally, draw the "icon"
qp.drawPixmap(rect, self._pixmap.scaled(rect.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
Here's a test window that will show the border-image implementation on top (along with its border splitting), and the subclass painting at the bottom.
class Test(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
layout.addWidget(QtWidgets.QLabel('basic border-image'), 0, 0, QtCore.Qt.AlignCenter)
borderButton = QtWidgets.QPushButton()
layout.addWidget(borderButton, 1, 0, QtCore.Qt.AlignCenter)
borderButton.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
borderButton.setFixedSize(120, 120)
borderButton.setStyleSheet('''
border-image: url(play.png);
''')
layout.addItem(QtWidgets.QSpacerItem(120, 0, QtWidgets.QSizePolicy.Expanding))
layout.addWidget(QtWidgets.QLabel('expanded border-image'), 0, 1, QtCore.Qt.AlignCenter)
borderButtonExpanded = QtWidgets.QPushButton()
layout.addWidget(borderButtonExpanded, 1, 1)
borderButtonExpanded.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
borderButtonExpanded.setStyleSheet('''
border-image: url(play.png) 60 60 60 60;
border-top: 60px transparent;
border-bottom: 60px transparent;
border-right: 60px transparent;
border-left: 60px transparent;
''')
layout.addItem(QtWidgets.QSpacerItem(0, 10))
layout.addWidget(QtWidgets.QLabel('paintEvent implementation'), 2, 0, 1, 2, QtCore.Qt.AlignCenter)
pmButton = PixmapButton()
layout.addWidget(pmButton, 3, 0, 1, 2)
pmButton.setMinimumSize(120, 120)
pmButton.setStyleSheet('''
QPushButton {
qproperty-pixmap: url(play.png);
/* hover mode: while there's no border shown, we can set the
default radius for the hover and pressed statuses */
border: none;
border-radius: 4px;
}
/* show border only if hovered or is checkable */
QPushButton:hover, QPushButton[checkable="true"] {
border: 1px outset green;
}
QPushButton:pressed, QPushButton:checked {
border: 1px inset green;
}
''')
pmButton2 = PixmapButton()
layout.addWidget(pmButton2, 4, 0, 1, 2)
pmButton2.setCheckable(True)
pmButton2.setMinimumSize(120, 120)
pmButton2.setStyleSheet(pmButton.styleSheet())
Note: This is a very simplified implementation. It does not check if the user sets a custom icon for the button, and there should be no button text at all.

How to make label text in jointjs elements not selectable?

i'm trying to find a solution for the following. When I drag a link between elements the label text inside the elements get selected for some reason.
Lets say I have an element A with the property A.attr("body/magnet", "active"); set and A.attr("label/text", "some text"); When I create a link from that element by clicking and dragging the label text gets selected on elements the link goes through.
This seems a little bit random though as sometimes all the labels in the graph gets selected when dragging the link.
Is there a way to make the label text not to be selectable?
We solved it by adding the following label style to the shapes.
let element = new joint.shapes.standard.Rectangle();
element.attr(
"label/style",
"-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;"
);
From the answer above the links will still be selected, so you can put css on you #paper is or canvas like so
#paper {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

How to add different color to odd and even rows in a pygtk TreeView

I have created a pygtk TreeView and wanted to add different colors between each line. I went here and it says that there exists a TreeView Style property that does exactly the same.The property is called 'odd-row-color' and 'even-row-color'. So went to my code and tried to apply this by using the set_property(). But i get an error message for doing that
self.customer_view.set_property('even-row-color', gtk.gdk.Color(211, 211, 211))
TypeError: object of type `GtkTreeView' does not have property `even-row-color'
How can achieve that. And where is that property handled?
You can use css (GTK3) to change the colors, something like:
style_provider = Gtk.CssProvider()
css = '''
GtkTreeView row:nth-child(even) { background-color: shade(#base_color, 0.9); }
GtkTreeView row:nth-child(odd) { background-color: shade(#base_color, 1.0); }
'''
style_provider.load_from_data(css.encode('utf8'))
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
Make sure to tell GTK that you want to paint alternate colors:
treeview.set_rules_hint(True)

Setting Selected property of Row in TreeView dynamically in Gtk3 (python)

This is the same problem as in my previous question, but I moved to python3/gtk3 to be able to use a css for setting the basic properties.
From the python file:
self.w = Gtk.Window()
self.w.set_name("App")
I can use a css:
#App GtkTreeView row:selected {
border-color: #000000;
border-top-width: 1px;
border-bottom-width: 1px;
color: #000;
}
And easily permanently change the style of the selection. To me this means that I should be able to dynamically get access to the row-object and its style where I could set the bg for the Gtk.StateFlags.SELECTED.
I've tried a bunch of weird ways, e.g (where bg_color is a Gdk.Color that works fine for e.g. changing the style of a Label outside the TreeView).
style=self.treeview.get_style_context()
col = style.get_background_color(Gtk.StateFlags.SELECTED)
col.alpha = 1.0
col.blue = bg_color.blue
col.red = bg_color.red
col.green = bg_color.green
Or:
style = self.treeview.get_style().copy()
style.bg[Gtk.StateFlags.SELECTED] = bg_color
self.treeview.set_style(style)
(produces error: style.bg[Gtk.StateFlags.SELECTED] = bg_color
IndexError: list assignment index out of range)
etcetera...
So please, how do I find the way to dynamically change the selection effect depending on the normal-color of the row? Or in other words, how do I find my way to the object that actually holds the style-setting for the selection?
I had one last idea about how it could be done after posting which actually ended up working:
Reloading the css dynamically:
In the css I added a row leaving the value for the background open to dynamic substitution:
#App GtkTreeView row:selected {
border-color: #400;
border-top-width: 2px;
border-bottom-width: 2px;
background: {0};
color: #000;
}
Then I loaded the css in python:
screen = Gdk.Screen.get_default()
self._css_provider = Gtk.CssProvider()
css = open("notify_stack.css", 'rb')
self._css = css.read()
css.close()
self._css_from = bytes("{0}".encode("utf8"))
self._css_provider.load_from_data(self._css.replace(
self._css_from,
bytes("#fff".encode("utf8"))))
context = Gtk.StyleContext()
context.add_provider_for_screen(screen, self._css_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
Later in the callback for when a row is selected I put this row (data is my ListStore):
self._css_provider.load_from_data(
self._css.replace(self._css_from,
bytes(data[rows[0]][self.BG_COLOR].encode("utf8"))))
It feels really brute, there must be a nicer way, but hey it actually worked.

PyQt : give a color to all the textof an application

I would like to change the look of my application in PyQt. I want all the text (in the buttons, labels and such) to be white for exampel, and all the buttons to be a certain color.
Can I change that all at once in the mainWindow ?
I did the following to change the background color of the whole app:
self.setStyleSheet("QMainWindow {background-color: #252526; color: #FFFFFF}")
If I set another stylesheet for the QPushButton for example in the same manner, the style for the QMainWindow will be overridden.
You could call the setStyleSheet() method on your QApplication instance and specify all object names in the CSS string:
app = QtGui.QApplication.instance()
app.setStyleSheet('QLabel{color: #fff;} QPushButton{background-color: #000; color: #fff}')

Resources