Mouse Over in Qt Designer - qt

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;
}

Related

Showing/hiding of a widget on focus at another widget

I would like "Button" object to disappear when "Target" object is not in focus (for example, when object "Secondary" is focused) and to re-appear when "Target" is in focus again. So, "Target" focused = "Button" visible. In other words, in the code below there are two lines, "Line A" and "Line B", that I would like to implement in the code.
`
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit
class Wn(QWidget):
def __init__(self):
super().__init__()
self.target = Target("Target", self)
self.target.setFixedSize(400, 60)
self.target.move(50, 50)
self.secondary = QLineEdit("Secondary", self)
self.secondary.setFixedSize(400, 60)
self.secondary.move(50, 150)
self.button = QPushButton("Appears # Target focused. Disappears # target not focused", self)
self.button.setFixedSize(400, 60)
self.button.move(50, 250)
class Target(QLineEdit):
def focusInEvent(self, k):
print("The target is in focus: Button should be shown")
self.setStyleSheet("background-color: red;")
# Wn.button.setHidden(False) # Line A
def focusOutEvent(self, p):
print("The target is out of focus: Button should be hidden")
self.setStyleSheet("background-color: white;")
# Wn.button.setHidden(True) # Line B
app = QApplication(sys.argv)
wn = Wn()
wn.show()
sys.exit(app.exec())
`
You can create a signal and emit it whenever the focus changes, then connect it with the button's setVisible().
class Wn(QWidget):
def __init__(self):
# ...
self.target.focusChanged.connect(self.button.setVisible)
class Target(QLineEdit):
focusChanged = pyqtSignal(bool)
def focusInEvent(self, k):
super().focusInEvent(k)
print("The target is in focus: Button should be shown")
self.setStyleSheet("background-color: red;")
self.focusChanged.emit(True)
def focusOutEvent(self, p):
super().focusOutEvent(p)
print("The target is out of focus: Button should be hidden")
self.setStyleSheet("background-color: white;")
self.focusChanged.emit(False)
Alternatively, you can just install an event filter on the line edit and look for FocusIn and FocusOut events.
Note that you should always call the base implementation of event handler overrides, unless you really know what you're doing, otherwise you might prevent proper default behavior of the object.
Also, layout managers should always be used instead of fixed geometries. Since the visibility of a widget also nullifies its size in the layout and adapts the other widgets managed by it (similarly to display: none in CSS), you should probably consider using setRetainSizeWhenHidden() for the widget's size policy:
class Wn(QWidget):
def __init__(self):
# ...
# create a proper layout and add widgets
# ...
policy = self.button.sizePolicy()
policy.setRetainSizeWhenHidden(True)
self.button.setSizePolicy(policy)

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)

pyqt - Custom QComboBox class style resets to default

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; }" );

change color of pushbutton when pushbutton is active

What is the best way to change to color of the button when pushbutton is active. for example ; pushbutton passive:its color is gray push button active :its color is green
setStyleSheet("QPushButton { background-color: gray; }\n"
"QPushButton:enabled { background-color: green; }\n");
You can apply this method on single button but I recommend to apply it on QAppliaction so it will have effect on all QPushButtons
https://qt-project.org/doc/qt-5/stylesheet-reference.html
Marek's answer is elegant. If you need to change more attributes than just the colors, subclass from PushButton and provide the required slot for pressed. Here's an example in Python (using PySide) that displays two different texts on the button, depending on whether it is active or not:
class TogglePushButtonWidget(QPushButton):
"""Toggles between on and off text
Changes color when in on state"""
def __init__(self, parent, on, off):
super().__init__(parent)
self.on = on
self.off = off
self.state = True
self.rotate_state()
self.pressed.connect(self.toggle_state)
def toggle_state(self):
self.state = not self.state
if self.state:
self.setText(self.on)
self.connect_w.setStyleSheet('background: #bbffbb;')
else:
self.setText(self.off)
self.connect_w.setStyleSheet('')

Resources