pyqt - Custom QComboBox class style resets to default - qt

I'm using a custom ComboBox class because I want to standardize a font for all my combo boxes. I've tried doing this in 3 different ways, as seen in my pyqt class definition below:
class StandardComboBox(QComboBox):
def _init_(self, parent = None):
super(StandardComboBox, self).__init__(parent)
self.setFont(QFont('Courier New', 30)) #<<< 1
self.setStyleSheet("font: 30pt \"Courier New\";") #<<< 2
def paintEvent(self, e):
painter = QStylePainter( self )
painter.setPen( self.palette().color( QPalette.Text ) )
opt = QStyleOptionComboBox()
opt.fontMetrics = QFontMetrics(QFont('Courier New', 30)) #<<<3
self.initStyleOption( opt )
painter.drawComplexControl( QStyle.CC_ComboBox, opt )
painter.drawControl( QStyle.CE_ComboBoxLabel, opt)
To call the class all I'm doing is:
self.myComboBox = StandardComboBox()
However, my combo boxes still have the default style and not the font I'm setting. What am I missing? Calling either of the #1 or #2 methods again on a combo box sets the font correctly, but that defeats the purpose of my custom class.

Remove everything, just leave self.setFont(QFont(...)) as it is, inside __init__. This worked for me. The problem with your code is that, you used single leading and trailing underscores for init method. Put double underscores, def __init__(self, parent=None)).
class StandardComboBox(QComboBox):
def __init__(self, parent = None):
super(StandardComboBox, self).__init__(parent)
self.setFont(QFont('Courier New', 30))
This code is enough to change the font of comboBox.

OR this works also:
.setStyleSheet( "QComboBox{ font: 14px 'monospace'; background-color: #fff; color: #000; border-style: solid; border-width: 1px; border-color: #000; border-radius: none; }" );

Related

QPalette and app.setStyleSheet() not impacting child Widget colors

I've got a pretty large PyQt5 application that I'm finally polishing with some colors. I've created a QPalette() and pass that to the app at launch. For the most part, it works (yay!). But not all of the child widgets are picking up the QPalette settings, and so I added a StyleSheet, but that again isn't working consistently. The only way I've been able to impact a widget color is by directly adding it when the widget is created, which is fine in something small.
The Main window:
class MyBigApplication(QMainWindow, QWidget):
def __init__(self):
super(MyBigApplication, self).__init__()
# ... load the various pieces, which include prompting user login and making some background connections.
# Build the GUI
def init_ui(self):
self.statusBar().showMessage('Welcome to MyBigApplication!')
self.grid_layout = QGridLayout()
# Initialize tab screen
self.tabs = QTabWidget()
self.tabs.setTabShape(QTabWidget.Triangular)
self.foo = fooTab(self, self.tabs)
self.bar = barTab(self, self.tabs)
self.baz = bazTab(self, self.tabs)
self.grid_layout.addWidget(self.tabs,0,1,4,1)
main_widget = QWidget()
main_widget.setLayout(self.grid_layout)
self.setCentralWidget(main_widget)
# Additional setup of menus and such
if __name__.endswith('__main__'):
app = QCoreApplication.instance()
while app is not None:
app.close()
app = QApplication(sys.argv)
app.setStyle('Fusion')
dark_palette = QPalette()
# Define some colors to get started
light_grey = QColor(243,243,243)
medium_grey = QColor(211,216,219)
dark_grey = QColor(52,59,64)
dark_palette.setColor(QPalette.Window, QColor(dark_grey))
dark_palette.setColor(QPalette.AlternateBase, QColor(medium_grey))
dark_palette.setColor(QPalette.Button, QColor(dark_grey))
dark_palette.setColor(QPalette.Base, QColor(25, 25, 25)) # almost black
dark_palette.setColor(QPalette.Link, QColor(green))
dark_palette.setColor(QPalette.Highlight, QColor(half_green))
dark_palette.setColor(QPalette.WindowText, QColor(light_grey))
dark_palette.setColor(QPalette.ToolTipBase, QColor(light_grey))
dark_palette.setColor(QPalette.ToolTipText, QColor(light_grey))
dark_palette.setColor(QPalette.Text, QColor(light_grey))
dark_palette.setColor(QPalette.ButtonText, QColor(light_grey))
dark_palette.setColor(QPalette.BrightText, Qt.red)
dark_palette.setColor(QPalette.HighlightedText, Qt.black)
app.setPalette(dark_palette)
app.setFont(QFont('Franklin Gothic Book', 9))
app.setStyleSheet("""
QMainWindow {
}
QToolTip {
color: #f3f3f3;
background-color: #2a82da;
border: 1px solid white;
}
QTableView { # This works
border: 1px solid #218a21
}
QPushButton { # And this works
padding: 10px 15px 10px 15px,
}
QPushButton:hover { # But this does not
background-color: red,
}
QTableView::item:alternate { # And this also does not
background-color: #d3d8db,
}
""")
execute = MyBigApplication()
sys.exit(app.exec_())
The fooTab includes tables of data:
class fooTab(QWidget):
def __init__(self, parent, tabs):
super(fooTab,self).__init__()
self.root = parent
self.tabs = tabs
def init_foo_one(self):
self.foo_tab = QWidget()
self.tabs.addTab(self.foo_tab, 'FOO')
tab_layout = QVBoxLayout()
foo_id_box = QGroupBox('FOO DATA')
clear_button = QPushButton('Clear Foo Data Table')
clear_button.clicked.connect(self.clear_foo_table)
# Set up the Table Model/View/Proxy
self.foo_id_table = QTableView()
self.foo_data_model = fooTableModel() # This is a QAbstractTableModel class
self.foo_data_model.setDataDict(data)
self.foo_id_table_columns = ['1','2','3','4']
self.foo_resizable_cols = [0,1,2,3,4]
self.foo_data_model.setDataHeader(self.foo_id_table_columns)
self.foo_table_proxy.setSourceModel(self.foo_data_model)
self.foo_id_table.setModel(self.foo_table_proxy)
self.foo_id_table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.foo_id_table.setSortingEnabled(True)
self.foo_id_table.setWordWrap(False)
self.foo_id_table.setAlternatingRowColors(True)
# Create a layout for that box using a grid
foo_id_box_layout = QGridLayout()
# Add the widgets into the layout
foo_id_box_layout.addWidget(self.foo_id_table,0,0,1,5)
foo_id_box_layout.addWidget(clear_button,1,2,1,1)
# Setup the layout to be displayed in the box
foo_id_box.setLayout(foo_id_box_layout)
tab_layout.addWidget(foo_id_box)
self.foo_tab.setLayout(tab_layout)
The bazTab:
class BazTab(QWidget):
def __init__(self, parent, tabs):
super(BazTab,self).__init__()
self.root = parent
self.tabs = tabs
self.h1_font = QFont()
self.h1_font.setBold(True)
self.h1_font.setPointSize(16)
self.h2_font = QFont()
self.h2_font.setBold(False)
self.h2_font.setPointSize(12)
self.h3_font = QFont()
self.h3_font.setBold(True)
self.init_ui()
def init_ui(self):
self.component_tab = QScrollArea()
self.tabs.addTab(self.baz_tab, 'Baz')
self.tab_layout = QHBoxLayout()
self.component_tab.setLayout(self.tab_layout)
component_button_box = QGroupBox('Various Buttons')
component_button_layout = QVBoxLayout()
component_button_layout.setAlignment(Qt.AlignTop)
component_button_box.setLayout(component_button_layout)
self.tab_layout.addWidget(component_button_box)
first_button = QPushButton('Request #1')
first_button.clicked.connect(self.request_one)
component_button_layout.addWidget(first_button)
second_button = QPushButton('Request #2')
second_button.clicked.connect(self.request_two)
component_button_layout.addWidget(second_button)
# Several more buttons created in here
# None of these buttons look like the buttons in the info dialog (which have a color from the QPalette)
I can manually edit the QTableView to show alternating colors, only if I add it to each instance.
I cannot get some of the QPushButtons to change, even when I add each instance's styleSheet.
Using the QPalette has saved a ton, by not having to modify widget by widget. For the extra details, I'm fine to use the app.setStyleSheet except that it doesn't always work.
Is it me, or is this just the way it is?

How to build a flat groupbox in PyQt5?

I found the flat property of the QGroupbox Class in the Qt-docs, where it says:
A group box usually consists of a surrounding frame with a title at the top. If this property is enabled, only the top part of the frame is drawn in most styles; otherwise, the whole frame is drawn.
I'm trying to build such a flat QGroupBox, but it doesn't seem to work, as you can see in this picture. In the docs it also says:
Note: In some styles, flat and non-flat group boxes have similar representations and may not be as distinguishable as they are in other styles.
That leads me to the QStyle Class, which overstrains me a little, as I have no idea how to change the Style of the QGroupBox. I'm not even sure, if this is the real problem here. As you can see in my code below, I don't assign a specific style to the QGroupBox. So, it should use some sort of standard style, i guess...
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QGroupBox,
QVBoxLayout, QLabel
class WidgetWithFlatGroupBox(QWidget):
def __init__(self):
QWidget.__init__(self, flags=Qt.Widget)
self.init_ui()
def init_ui(self):
self.setGeometry(300, 300, 300, 100)
self.setWindowTitle('PyQt5 Window')
layout = QVBoxLayout()
layout.addWidget(self.create_groupbox(), alignment=Qt.AlignCenter)
self.setLayout(layout)
def create_groupbox(self):
groupbox = QGroupBox('Flat Groupbox')
layout = QVBoxLayout()
label = QLabel('This Groupbox should be flat.')
layout.addWidget(label, alignment=Qt.AlignCenter)
groupbox.setLayout(layout)
groupbox.setFlat(True)
return groupbox
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = WidgetWithFlatGroupBox()
ex.show()
sys.exit(app.exec_())
I had a look at the internet, also here at stackoverflow of course, but were not able to find something useful. Is there anyone who knows how to use the flat property of the QGroupBox Class properly?
Sorry, but I do not understand how you need to do:
border: None;
border: 1px solid #76797C;
Try it:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QWidget, QGroupBox,
QVBoxLayout, QLabel, QPushButton)
class WidgetWithFlatGroupBox(QWidget):
def __init__(self):
QWidget.__init__(self, flags=Qt.Widget)
self.init_ui()
def init_ui(self):
self.setGeometry(300, 300, 300, 100)
self.setWindowTitle('PyQt5 Window')
layout = QVBoxLayout()
layout.addWidget(self.create_groupbox(), alignment=Qt.AlignCenter)
self.setLayout(layout)
def create_groupbox(self):
groupbox = QGroupBox('Flat Groupbox')
layout = QVBoxLayout()
label = QLabel('This Groupbox should be flat.')
button = QPushButton("This Button !!!")
button.setFlat(True)
layout.addWidget(label, alignment=Qt.AlignCenter)
layout.addWidget(button, alignment=Qt.AlignCenter)
groupbox.setLayout(layout)
# groupbox.setFlat(True)
return groupbox
style = '''
QGroupBox {
border: None; /*<----- None */
/* border: 1px solid #76797C; <----- 1px solid #76797C */
border-radius: 2px;
margin-top: 20px;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top center;
padding-left: 10px;
padding-right: 10px;
padding-top: 10px;
}
'''
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyleSheet(style)
ex = WidgetWithFlatGroupBox()
ex.show()
sys.exit(app.exec_())
self.groupBox_33.setFlat(True)

Changing background color of a QWidget is not possible with a css class and setProperty()

In a sublclass of a QWidget:
class MainWidget(QtGui.QWidget):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
...
self.setStyleSheet(stylesheet)
works just fine with this css:
QWidget {
background-color: black;
}
but adding this:
self.setStyleSheet(stylesheet)
self.setProperty('class', 'main')
and changing the css to:
QWidget.main {
background-color: black;
}
shows no effect at all. What am i missing on this one? setProperty works like a charm on other Objects.
If you subclass a custom widget from QWidget, then in order to use the StyleSheets you need to provide a paintEvent to the custom widget. See this page.
So add the following method to your MainWidget class:
def paintEvent(self, event):
"Reimplementation of paintEvent to allow for style sheets"
opt = QtGui.QStyleOption()
opt.initFrom(self)
painter = QtGui.QPainter(self)
self.style().drawPrimitive(QtGui.QStyle.PE_Widget, opt, painter, self)
painter.end()

Cell spacing and focus rectangle in QTableView

I have a QTableView that shows some data from my custom subclass of QAbstractTableModel. The problem is that the text is pushed up against the left side of the cell. I found this question: How to set the padding of QTableView cells through CSS? It led me to try this:
self.tableView.setStyleSheet('QTableView::item {border: 0px; padding: 5px;} ')
That indented the text but introduced a new problem. Now when I click on a cell, the dotted focus rectangle is inset into the cell.
How can I have both cell spacing and a dotted focus rectangle that surrounds the entire cell (not inset)?
Using CSS is not the best way to do this, take 5 minutes of your time and write delegate, it's easy.
But you can try:
QTableView {
outline: 0; /* Disable focus rect*/
}
QTableView:item {
border: 0px;
padding: 0px 10px;
}
QTableView::item:focus { /*Emulate focus*/
color: black;
background-color: yellow;
border: 1px dashed black;
}
Here is what I was finally able to come up with. It puts the focus rectangle on the border of each cell, regardless of "padding" or "margin". It also preserves the stylesheet. At least it preserves background color and padding. I didn't test all stylesheet options. However, it does not preserve the text color in the cell with focus. (P.S. This is working with PySide 1.1.1)
class CellDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent):
super(CellDelegate, self).__init__(parent)
self._parent = parent
def paint(self, qPainter, option, qModelIndex):
v4Option = QtGui.QStyleOptionViewItemV4(option)
v4Option.index = qModelIndex
value = qModelIndex.data()
v4Option.text = str(value)
style = self._parent.style()
if (v4Option.state & QtGui.QStyle.State_HasFocus):
# --- The table cell with focus
# Draw the background
style.drawPrimitive(style.PE_PanelItemViewItem, v4Option, qPainter, self._parent)
# Draw the text
subRect = style.subElementRect(style.SE_ItemViewItemText, v4Option, self._parent)
alignment = qModelIndex.data(QtCore.Qt.TextAlignmentRole)
if not alignment:
alignment = int(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
if (v4Option.state & QtGui.QStyle.State_Enabled):
itemEnabled = True
else:
itemEnabled = False
textRect = style.itemTextRect(v4Option.fontMetrics, subRect, alignment, itemEnabled, value)
style.drawItemText(qPainter, textRect, alignment, v4Option.palette, v4Option.state, value)
# Draw the focus rectangle
focusOption = QtGui.QStyleOptionFocusRect()
focusOption.rect = v4Option.rect
style.drawPrimitive(style.PE_FrameFocusRect, focusOption, qPainter, self._parent)
else:
# --- All other table cells
style.drawControl(style.CE_ItemViewItem, v4Option, qPainter, self._parent)
Here is some example code showing how to use it. The goal is that the stylesheet would be set in a .ui file. This is just a self-contained example:
class TestTableModel(QtCore.QAbstractTableModel):
headerNames = ('Column 1', 'Column 2')
def __init__(self):
super(TestTableModel, self).__init__()
self._data = [['test', 'text'], ['yyy', 'zzz']]
#----- Overridden Functions ------------------------------------------------
def columnCount(self, parentIndex):
return len(self.headerNames)
def data(self, qModelIndex, role=QtCore.Qt.DisplayRole):
if qModelIndex.isValid():
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
dataItem = self._data[qModelIndex.row()][qModelIndex.column()]
return dataItem
return None
def headerData(self, colNum, orientation, role):
if (orientation == QtCore.Qt.Horizontal) and (role == QtCore.Qt.DisplayRole):
return self.headerNames[colNum]
return None
def rowCount(self, parentIndex=QtCore.QModelIndex()):
return len(self._data)
#------------------------------------------------------------------------------
class TestTableViewSpacing(QtGui.QMainWindow):
def __init__(self, parent=None):
super(TestTableViewSpacing, self).__init__(parent)
self.tableView = QtGui.QTableView()
self.setCentralWidget(self.tableView)
tableModel = TestTableModel()
self.tableView.setModel(tableModel)
self.tableView.setStyleSheet('QTableView::item {border: 0px; padding: 5px; margin: 5px; color: yellow; '
'background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #330055, stop: 1 #000000);} '
'QTableView::item:focus {border: 0px; background-color: darkred; color: yellow;}')
# VERY IMPORTANT!! Must pass the table view to the delegate, or it will not work!
newDelegate = CellDelegate(self.tableView)
self.tableView.setItemDelegate(newDelegate)

Mouse Over in Qt Designer

I made the gui in Qt Designer and then converted it into python using pyuic4. Now i want to capture the mouseover event on the buttons.
class window_b(QtGui.QDialog):
def __init__(self,parent=None):
super(window_b, self).__init__(parent)
window_a.setEnabled(False)
self.ui = Ui_Form_window_b()
self.ui.setupUi(self)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def mouseMoveEvent (self,event):
source= self.sender()
#print source.name()
# The action I want to do when the mouse is over the button:
source.setStyleSheet("background-color:#66c0ff;border-radiu‌​s: 5px;")
I put the mouseMoveEvent method on the widget and I want to detect which button on the Dialog sent the mouseOver event. I tried source.name() but it throws me this error
print source.name()
AttributeError: 'NoneType' object has no attribute 'name'
Any suggestion.
sender() is only useful for signals but the mouse hovering is an event not a signal (actually 2 events: QEvent.Enter and QEvent.Leave).
And to be able to handle events outside the buttons that received them, you need to install your window_b instance as an event filter for each button.
class window_b(QtGui.QDialog):
def __init__(self,parent=None):
super(window_b, self).__init__(parent)
window_a.setEnabled(False)
self.ui = Ui_Form_window_b()
self.ui.setupUi(self)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
# Get all the buttons (you probably don't want all of them)
buttons = self.findChildren(QtGui.QAbstractButton)
for button in buttons:
button.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.Enter:
print("mouse entered %s" % obj.objectName())
elif event.type() == QtCore.QEvent.Leave:
print("mouse leaved %s" % obj.objectName())
return super(window_b, self).eventFilter(obj, event)
If you only need to change the style, you can simply use the pseudo-state ":hover" in a stylesheet (from the designer, or in the constructor with self.setStyleSheet):
QPushButton {
border: 1px solid black;
padding: 5px;
}
QPushButton:hover {
border: 1px solid black;
border-radius: 5px;
background-color:#66c0ff;
}

Resources