How do I get the font size and color of the text in the tooltip to change from that of the button? It keeps displaying as the size/font of the pushbutton instead of it's own. Also how do I get the text "leave the program" to actually fit in the tooltip?
I've tried this method and couldn't get it to work:
Setting the text colour of a tooltip in PyQt
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(100, 100)
self.setWindowTitle("Example")
self.leave = QPushButton("X", self)
self.leave.setStyleSheet("background-color : grey ; color : red ; font: 20pt")
self.leave.setToolTip("Leave the Program")
self.setStyleSheet("QToolTip{background-color : blue ; color: k ; font: 12pt}")
self.leave.move(38, 25)
self.leave.resize(24, 50)
self.leave.clicked.connect(self.exit)
def exit(self):
app.quit()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
The tool tip in question is a child of the button not of the main window so it will inherit the style sheet of the button (as well as those of the button's ancestors). Since you didn't specify a selector in the style sheet of the button, the style rules in this style sheet will be applied to all the button's children including the tool tip (unless they have a style sheet themselves). One way around this is to limit the button's style sheet to QPushButton objects only by doing something like
self.leave.setStyleSheet("QPushButton{background-color : grey ; color : red ; font: 20pt}")
Furthermore, to get the background color of the tool tip to change from default I needed to specify a style rule for its border, e.g.
self.setStyleSheet(" QToolTip{ border: 1px solid white; background-color: blue ; color: k ; font: 12pt}")
I am not sure if this is a bug or by design.
Screenshot:
Related
How to align two widgets more closely? In my code, I want to align QLabel 1 and QLabel 2 more closely (i.e. QLabel 2 aligned just below the QLabel 1, with minimum spacing).
import sys
from PyQt5 import QtCore,QtGui,QtWidgets
class Layout_sample(QtWidgets.QWidget):
def __init__(self):
super(). __init__()
self.setWindowTitle("Layout Sample")
self.vbox = QtWidgets.QVBoxLayout()
self.lbl1 = QtWidgets.QLabel("F3")
self.lbl2 = QtWidgets.QLabel(u'\u2550'+u'\u2550')
self.vbox.addStretch()
self.vbox.addWidget(self.lbl1)
self.vbox.addWidget(self.lbl2)
self.vbox.addStretch()
self.vbox.setSpacing(0)
self.setLayout(self.vbox)
if __name__ =="__main__":
app = QtWidgets.QApplication(sys.argv)
mainwindow = Layout_sample()
mainwindow.show()
sys.exit(app.exec_())
I assume what you're trying to achieve is a double underline for the text in the first label. The problem with your example is that the unicode character ═ (U+2550) is centered vertically, so there will always be some fixed space above it. The unicode box-drawing characters don't include a top-aligned double-underline, so a different approach is needed.
One solution is to use html/css inside the label to draw a double border below the text. This has to be done using a table-cell, because Qt only supports a limited subset of html/css:
underline = """<td style="
border-bottom-style: double;
border-bottom-width: 3px;
">%s</td>"""
self.lbl1 = QtWidgets.QLabel(underline % 'F3')
self.vbox.addStretch()
self.vbox.addWidget(self.lbl1)
self.vbox.addStretch()
I want to change the color of selected item in QListItem, and I find qss may be a solution. And the code is:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
with open('mainStyle.qss', 'r', encoding='utf-8') as file:
self.setStyleSheet(file.read())
# self.setStyleSheet('*{font-size: 15px;background-color: rgb(150, 150, 150);}'
# 'QListWidget::item:selected{background: rgb(128,128,255);}')
self.setStyleSheet('QListWidget::item:selected{background: rgb(128,128,255);}')
layout = QVBoxLayout()
self.setLayout(layout)
listWidget = QListWidget()
layout.addWidget(listWidget)
w1 = QWidget()
w1Item = QListWidgetItem()
w1Item.setSizeHint(QSize(150, 150))
listWidget.insertItem(0, w1Item)
listWidget.setItemWidget(w1Item, w1)
w2 = QWidget()
w2Item = QListWidgetItem()
w2Item.setSizeHint(QSize(150, 150))
listWidget.insertItem(1, w2Item)
listWidget.setItemWidget(w2Item, w2)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
win.show()
app.exec_()
We can see that the color is changed to be blue when the item is selected.
However, I need to provide a general background color for other widgets. So I change the style from
self.setStyleSheet('QListWidget::item:selected{background: rgb(0,0,255);}')
to
self.setStyleSheet('*{font-size: 15px;background-color: rgb(150, 150, 150);}'
'QListWidget::item:selected{background: rgb(0,0,0);}')
Then, I find QListWidget::item:selected do not work. The color do not change when I select a item.
What's the wrong with my code?
The problem is that you're setting a QWidget for the item, and since you're using a universal selector (with the wildcard), the result is that all QWidget will have that background color, including those added as item widgets for the list view.
The color you used for the :selected pseudo is only valid for the item painted by the view, since the item widget has its own background set from the universal selector, that background won't be visible.
The solution is to use a proper selector combination ensuring that the rule only matches children of the list view that have a usable selector, and set a transparent color for those widgets.
A possibility is to set a custom property for the widgets that must be set before adding the widgets to the list (otherwise, you need to set the stylesheet after adding them, or request a style.unpolish()).
self.setStyleSheet('''
QWidget {
font-size: 15px;
background: rgb(150, 150, 150);
}
QListWidget::item:selected {
background: rgb(128,128,255);
}
QListWidget QWidget[widgetItem=true] {
background: transparent;
}
''')
# ...
w1 = QWidget()
w1.setProperty('widgetItem', True)
# ...
w2 = QWidget()
w2.setProperty('widgetItem', True)
# ...
Another way to do so is to use an "empty" subclass for widgets that are going to be added to the item view:
class CustomItemWidget(QWidget): pass
class Window(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet('''
QWidget {
font-size: 15px;
background: rgb(150, 150, 150);
}
QListWidget::item:selected {
background: rgb(128,128,255);
}
CustomItemWidget {
background: transparent;
}
''')
# ...
w1 = CustomItemWidget()
# ...
w2 = CustomItemWidget()
# ...
Consider that using universal selectors for colors is usually not a good idea, as it can result in inconsistent behavior, especially for complex widgets: for instance, the scroll bars of scroll areas (like QListWidget) might not be correctly styled and can even become unusable.
If you plan to have a common background color for all widgets, it's better to set the Window role of the application palette:
app = QApplication(sys.argv)
palette = app.palette()
palette.setColor(palette.Window, QColor(150, 150, 150))
app.setPalette(palette)
# ...
In this way the current style will know exactly when to use that color as background, or as a component for other UI elements.
I have to create a custom widget that looks like the following:
custom_widget_sketch
Each custom widget is a representation of one LIPO battery, and displays the battery volatge (V), status text (charging, discharging, etc), serial number of the battery (S/N) and three status LEDs (yellow, green and red)
After I have created the custom widget I need to add 30 of these in a grid of 6*5. My assumption here is that once I have created that custom widget it should be as simple as adding say a QPushButton in a QGridLayout like so:
custom_layput = QGridLayout()
custom_layout.addWidget(custom_widget, 0, 0)
custom_layout.addWidget(custom_widget, 0, 1)
.
.
.
custom_layout.addWidget(custom_widget, 6, 5)
The final screen would look like this:
main_window_sketch
Considering all of these requirements I have the following questions:
Will I able able to create such a complex/rich custom widget using PyQt5? Is it doable?
Is this the correct approach to create the custom widget: first draw a square using QPainter (this is the blue square in the custom_widget_sketch), then add QLineEdits to dispaly the voltage (V) text, serial number (S/N) text and the Status text, add a QLabel for displaying the "V", "S/N" and "STATUS" labels in the custom widget, then draw the three circles: one each for the yellow, green and red LEDs, then use a combination of QVBoxLayout and QHBoxLayout to arrange the QLineEdits, QLabels, the square and the circles (LED indicators)
How do I package this custom widget such that I can simply add it to a layout like I would add a QPushButton or a QLineEdit?
PS: The custom_widget_sketch also contains a line and a square with three lines inside it in the top left corner. This is to depict the connector for the LIPO battery. It may be too complex to implement that right now. So I would be happy even if I am able to implement everything other than these two elements
I have been through a few SO questions but they all refer to one tutorial, which is not my end goal.
I would appreciate any code snippets, general outline of code/steps to follow or links to any articles/tutorials that create custom widgets similar to the one I wish to create.
Python code for the custom widget I ended up creating. The widget looks as follows:
from PyQt5.QtGui import QPainter, QPen,QBrush,QColor
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout,QPushButton, QLineEdit, QLabel, QVBoxLayout, QHBoxLayout, QSizePolicy, QGroupBox
import sys
class BatteryStatusWidget(QWidget):
def __init__(self):
super(BatteryStatusWidget, self).__init__()
#Voltage widgets
self.voltage_text = QLineEdit()
self.voltage_text.setReadOnly(True)
self.voltage_label = QLabel("V")
self.voltage_label.setStyleSheet("QLabel {color : white}")
#Status widgets
self.status_text = QLineEdit()
self.status_text.setReadOnly(True)
self.status_label = QLabel("STATUS")
self.status_label_font = QtGui.QFont()
self.status_label_font.setPointSize(12)
self.status_label.setFont(self.status_label_font)
self.status_label.setAlignment(QtCore.Qt.AlignCenter)
self.status_label.setStyleSheet("QLabel {color : white}")
#Serial number
self.serial_number_text = QLineEdit()
self.serial_number_label = QLabel("S/N")
#LED widgets
self.yellow_led_label = QLabel()
self.yellow_led_label.setStyleSheet("QLabel {background-color : yellow; border-color : black; border-width : 1px; border-style : solid; border-radius : 10px; min-height: 20px; min-width: 20px}")
self.green_led_label = QLabel()
self.green_led_label.setStyleSheet("QLabel {background-color : green; border-color : black; border-width : 1px; border-style : solid; border-radius : 10px; min-height: 20px; min-width: 20px}")
self.red_led_label = QLabel()
self.red_led_label.setStyleSheet("QLabel {background-color : red; border-color : black; border-width : 1px; border-style : solid; border-radius : 10px; min-height: 20px; min-width: 20px}")
#Number Identifier Label
#This label is for tagging the widget with the same label as on the PCB
self.number_label = QLabel("Test")
self.number_label.setAlignment(QtCore.Qt.AlignCenter)
self.number_label_font = QtGui.QFont()
self.number_label_font.setPointSize(12)
self.number_label_font.setBold(True)
self.number_label.setFont(self.number_label_font)
#Layouts
#voltage layout
self.voltage_layout = QHBoxLayout()
self.voltage_layout.addWidget(self.voltage_text)
self.voltage_layout.addWidget(self.voltage_label)
#Serial number layout
self.serial_num_layout = QHBoxLayout()
self.serial_num_layout.addWidget(self.serial_number_label)
self.serial_num_layout.addWidget(self.serial_number_text)
#Voltage and status box layouts
self.blue_container = QWidget()
self.blue_container.setStyleSheet("background-color:rgb(77, 122, 194);")
self.blue_box_layout = QVBoxLayout()
self.blue_box_layout.addLayout(self.voltage_layout)
self.blue_box_layout.addWidget(self.status_text)
self.blue_box_layout.addWidget(self.status_label)
self.blue_container.setLayout(self.blue_box_layout)
#Blue box+ serial num layout
self.non_led_layout = QVBoxLayout()
#self.non_led_layout.addWidget(self.number_label)
self.non_led_layout.addWidget(self.blue_container)
self.non_led_layout.addLayout(self.serial_num_layout)
#LED layout
self.led_layout = QVBoxLayout()
self.led_layout.addWidget(self.yellow_led_label)
self.led_layout.addWidget(self.green_led_label)
self.led_layout.addWidget(self.red_led_label)
self.led_layout.addStretch(1)
#Main Layout
self.main_layout = QHBoxLayout()
self.main_layout.addLayout(self.non_led_layout)
self.main_layout.addLayout(self.led_layout)
#Main group box
self.main_group_box = QGroupBox()
self.main_group_box.setStyleSheet("QGroupBox{font-size: 10px}")
self.main_group_box.setTitle("Chan #")
self.main_group_box.setLayout(self.main_layout)
#Outer main layout to accomodate the group box
self.outer_main_layout = QVBoxLayout()
self.outer_main_layout.addWidget(self.main_group_box)
#Set the main layout
self.setLayout(self.outer_main_layout)
self.setWindowTitle("Battery Widget")
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = BatteryStatusWidget()
main_window.show()
app.exec_()
I was able to easily create 30 instances of the custom widget and add it to a QGridLayout as I posted in my question. The final GUI screen looks as follows:
There's no need to use QPainter for the blue square, as you can use stylesheets for your whole widget, the trick is to use selectors.
I tried to create your widget and used this stylesheet:
Battery {
background-color: white;
}
QFrame#statusFrame {
background-color: rgb(64, 112, 190);
}
QFrame#statusFrame QLabel {
color: white;
font-weight: bold;
font-size: 24pt;
}
QLineEdit {
border: 1px solid black;
font-size: 24pt;
}
#serialLabel {
font-weight: bold;
font-size: 16pt;
}
I created a "container" QWidget, the status rectangle is actually a QFrame with its own layout, which I named statusFrame (you can set it in designer, or by means of setObjectName(str)).
By using object names and child selectors, I was able to set specific fonts for its labels by using the QFrame#statusFrame QLabel selector (which means "apply to each QLabel that is a child of a QFrame"); I also set the serialLabel object name to the s/n label, allowing me to set a different font size.
You can do this from code or using designer, just remember to set the right object names and parent/children hierarchy.
I was able to draw the "connector" part too, which requires to set fixed margins to the main widget:
class Battery(QtWidgets.QWidget):
connPath = QtGui.QPainterPath()
connPath.addRect(30, 10, 40, 28)
connPath.moveTo(30, 16)
connPath.lineTo(45, 16)
connPath.moveTo(30, 24)
connPath.lineTo(45, 24)
connPath.moveTo(30, 32)
connPath.lineTo(45, 32)
cablePen = QtGui.QColor(77, 122, 194)
def __init__(self):
QtWidgets.QWidget.__init__(self)
# the following is only if you create the whole widget from code,
# otherwise ensure to set both widget and layout contentsMargins
# accordingly in designer
self.setContentsMargins(25, 50, 10, 10)
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
layout.setContentsMargins(0, 0, 0, 0)
# ...
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
qp.translate(.5, .5)
qp.drawPath(self.connPath)
qp.setPen(self.cablePen)
cablePath = QtGui.QPainterPath()
cablePath.moveTo(30, 24)
top = self.statusFrame.geometry().top()
cablePath.quadTo(0, top + 20, 25, top + 40)
qp.drawPath(cablePath)
As you can see, it's almost the same as yours.
I'm trying to make a side panel with pyqt. I'm able to make nice looking buttons but not able to fill the rest of the space with the same color.
My goal is to add the same blue color to space under the buttons, all the way to the bottom. Now my buttons are inside of a box layout which is inside of a widget (just to be able to set the style) which is inside of a grid cell which is inside of a widget.
I tried to add one more widget to the box layout, but it is not stretching to fill the space. I can set minimum height, but then if the window is resized, the blue area won't resize.
import sys
import PySide2.QtWidgets as qt
from PySide2 import QtCore, QtGui
import os
class main_page:
def create(self):
# Create main window
main_window = qt.QMainWindow()
# Set some parameters for main window
main_window.setGeometry(50,50,700,400)
main_window.setWindowTitle("Here will be name of the program")
# Create central widget that will contain layout to contain widgets. Eh...
central_widget = qt.QWidget()
central_widget.setStyleSheet("background: white;")
# Add central widget to main window
main_window.setCentralWidget(central_widget)
# Create main grid layout that will organize everything
main_grid = qt.QGridLayout(central_widget)
main_grid.setSpacing(0)
main_grid.setMargin(0)
# Create widget that will contain layout and now we can set style for the widget.
widget_that_would_not_be_needed_if_qt_would_not_be_so_stupid = qt.QWidget()
widget_that_would_not_be_needed_if_qt_would_not_be_so_stupid.setStyleSheet(
'''
QPushButton {
background: #FF005AA1;
color: white;
text-align: center;
border: none;
font-weight: 500;
font-size: 15px;
height: 48px;
width: 120px;
}
QPushButton:hover {
background-color: #FF014a82;
}
QPushButton:checked {
background: #FF01508c;
}
QPushButton:pressed {
color: #FF005AA1;
background: white;
}
''')
# On left side we will have some (or at least one) button
left_side_buttons = qt.QVBoxLayout(widget_that_would_not_be_needed_if_qt_would_not_be_so_stupid)
left_side_buttons.setSpacing(0)
left_side_buttons.setMargin(0)
# And add it to main box the left most cell
main_grid.addWidget(widget_that_would_not_be_needed_if_qt_would_not_be_so_stupid, 0, 0, alignment = QtCore.Qt.AlignTop)
main_grid.setRowStretch(0,1)
# Column 0 must not stretch
main_grid.setColumnStretch(0,0)
# Column 1 must stretch
main_grid.setColumnStretch(1,1)
# Add widgets to container
left_side_buttons.addWidget(qt.QPushButton("First button"))
left_side_buttons.addWidget(qt.QPushButton("Second button"))
# Add separator line
line = qt.QFrame()
line.setFrameShape(qt.QFrame.HLine)
line.setFrameShadow(qt.QFrame.Sunken)
left_side_buttons.addWidget(line)
# Add color for rest of the space
color_filler = qt.QWidget()
# I can use this to hack to make colored area bigger but it won't stretch
color_filler.setMinimumHeight(100)
left_side_buttons.addWidget(color_filler)
main_grid.addWidget(qt.QListView(), 0, 1)
main_window.show()
# Add list view to right side
main_grid.addWidget(qt.QListView(), 0, 1)
main_window.show()
return main_window
def main():
app = qt.QApplication(sys.argv)
my_main_page = main_page().create()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Here's what I get and what I want:
Probably the easiest way to get what you want, is to do the following
Add QObject { background: #FF005AA1; } to the style sheet of widget_that_would_not_be_needed_if_qt_would_not_be_so_stupid.
Remove the alignment argument when you add widget_that_would_not_be_needed_if_qt_would_not_be_so_stupid to main_grid (or use alignment = QtCore.Qt.AlignJustify),
Replace color_filler with a stretchable space at the bottom of left_side_buttons (a stretchable space can be added via left_side_buttons.addStretch()).
Screenshot:
I'm looking to change the colour of the Value text from default black to white.
I've got a stylesheet, but the color: white only seems to apply to the text, not the value text.
'''
Load UI
'''
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
app.setStyleSheet("""
QMainWindow {background-color: rgb(80,80,80); color: white;}
QProgressDialog {background-color: rgb(80,80,80); color: white;}
""")
sys.exit(app.exec_())
I've also tried adding the QProgressBar to the stylesheet, but this didn't do anything either.
This is my code for the progress dialog:
def pathCasingFixFUNC(self):
'''
Fixes the path files that were named badly in notepad ++
'''
self.XprobBadCasing = ["List of bad file paths i'm checking"]
Xprogress = QtGui.QProgressDialog("Converting Path Names...", "Cancel", 0, len(self.XprobBadCasing), self)
Xprogress.setWindowModality(QtCore.Qt.WindowModal)
Xprogress.setWindowTitle('Processing')
Can i set the value font colour using Xprogress.value.setFont() or something?