QTableWidget set the Background of header Cell - qt

I know how to set the Horizontal Headers background color, but how can i set the Headers Background Color for a specified Row? In this example, say i want to have the Header Row with the Label "3" to be red?
The above screenshots code:
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import *
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.rigTable = QTableWidget(1, 3, self)
style = "::section {""background-color: lightblue; }"
self.rigTable.horizontalHeader().setStyleSheet(style)
self.rigTable.setShowGrid(False)
self.rigTable.setCellWidget(0, 0, QLabel("A"))
self.rigTable.setCellWidget(0, 1, QLabel("B"))
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

You can use QTableWidgetItem class:
header = QtGui.QTableWidgetItem("3")
header.setBackground(QtGui.QColor("red"))
self.rigTable.setHorizontalHeaderItem(2, header)

Related

How to add dynamically stretching gap between widgets in Qt?

I want to add spaces between the widgets, dynamically, so that, as i resize the window, the widgets get their gaps adjusted accordingly, so that the widgets will be evenly spread over the available window space.
#!/usr/bin/python3
import sys
import random
try:
from PySide6 import QtCore, QtWidgets
except ModuleNotFoundError:
from PySide2 import QtCore, QtWidgets
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QVBoxLayout(self)
for i in range(0, 10):
self.text = QtWidgets.QLabel("Hello World",
alignment=QtCore.Qt.AlignCenter,
styleSheet = f'''
background-color: '#999999';
color : '#000000';
'''
)
self.layout.addWidget(self.text)
self.layout.addStretch()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = MyWidget()
widget.resize(800, 600)
widget.show()
sys.exit(app.exec_())
If you set labels' vertical size policy to QtWidgets.QSizePolicy.Maximum or QtWidgets.QSizePolicy.Fixed and remove self.layout.addStretch() you'll get evenly spread stretch.
self.text.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
Alternatively you can add self.layout.addStretch() after every label to achieve same thing.

Change highlight color for a specific item (QAction type) on a QMenuBar

I want to mimic the behavior of the window top-right corner actions when hovering over the minimize-maximize-close buttons.
So I have created a QMenuBar that contains 3 QActions.
class WindowMenuActions(QMenuBar):
def __init__(self):
super(WindowMenuActions, self).__init__()
minimize_action = QAction(self)
minimize_action.setIcon(_MINIMIZE_ICON)
maximize_action = QAction(self)
maximize_action.setIcon(_MAXIMIZE_ICON)
exit_action = QAction(self)
exit_action.setIcon(_CLOSE_ICON)
self.addAction(minimize_action)
self.addAction(maximize_action)
self.addAction(exit_action)
And set the background color when hovering with stylesheet (it is applied to the entire QApplication):
I want the close button to have a different highlight color, so I tried setting a boolean property on the exit_action to change the color only for that specific item, but it does not do any effect (in fact, if I add the property in the stylesheet it doesn't even consider the stylesheet values)
Any idea of how to achieve this?
To extend the question, I would not only use this for the minimize-maximize-close actions, but also to understand how to apply different hover/selected colors on QActions in the QMenuBar (not in the QMenu, which I already found a solution for that).
This is an running example:
from PySide2.QtWidgets import QMenuBar, QAction, QStyle, QApplication, QMainWindow
from PySide2.QtCore import Qt
class WindowMenuActions(QMenuBar):
def __init__(self):
super(WindowMenuActions, self).__init__()
minimize_action = QAction(self)
_MINIMIZE_ICON = self.style().standardIcon(QStyle.SP_TitleBarMinButton)
_MAXIMIZE_ICON = self.style().standardIcon(QStyle.SP_TitleBarMaxButton)
_EXIT_ICON = self.style().standardIcon(QStyle.SP_TitleBarCloseButton)
minimize_action.setIcon(_MINIMIZE_ICON)
minimize_action.setProperty('exit_action', False)
maximize_action = QAction(self)
maximize_action.setProperty('exit_action', False)
maximize_action.setIcon(_MAXIMIZE_ICON)
exit_action = QAction(self)
exit_action.setProperty('exit_action', True)
exit_action.setIcon(_EXIT_ICON)
self.addAction(minimize_action)
self.addAction(maximize_action)
self.addAction(exit_action)
self.setStyleSheet(
'QMenuBar::item:selected {'
'background-color: grey;'
'}'
'QMenuBar::item[exit_action=true]:selected {'
' background-color: red;'
'}')
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
main = QMainWindow()
main.setMenuBar(WindowMenuActions())
main.show()
sys.exit(app.exec_())
To anyone interested, I sorted by inheriting from a regular QWidget instead of a QMenuBar and using QToolButton instead of QAction.
Working example:
from PySide2.QtWidgets import QMenuBar, QStyle, QApplication, QMainWindow, QWidget, QHBoxLayout, QToolButton, QMenu
from PySide2.QtCore import Qt
class MenuBar(QMenuBar):
def __init__(self):
super(MenuBar, self).__init__()
self.addMenu(QMenu('File'))
self.addMenu(QMenu('Help'))
self.setCornerWidget(WindowMenuActions(self))
class WindowMenuActions(QWidget):
def __init__(self, parent=None):
super(WindowMenuActions, self).__init__(parent)
_MINIMIZE_ICON = self.style().standardIcon(QStyle.SP_TitleBarMinButton)
_MAXIMIZE_ICON = self.style().standardIcon(QStyle.SP_TitleBarMaxButton)
_EXIT_ICON = self.style().standardIcon(QStyle.SP_TitleBarCloseButton)
minimize = QToolButton(self)
minimize.setIcon(_MINIMIZE_ICON)
maximize = QToolButton(self)
maximize.setIcon(_MAXIMIZE_ICON)
exit_action = QToolButton(self)
exit_action.setProperty('exit_button', True)
exit_action.setIcon(_EXIT_ICON)
layout = QHBoxLayout()
layout.addWidget(minimize)
layout.addWidget(maximize)
layout.addWidget(exit_action)
self.setLayout(layout)
self.setStyleSheet(
'QToolButton:hover {'
' background: grey;'
'}'
'QToolButton[exit_button=true]:hover {'
' background: red;'
'}'
)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
main = QMainWindow()
main.setWindowFlags(Qt.FramelessWindowHint)
main.setMenuBar(MenuBar())
main.show()
sys.exit(app.exec_())

Preventing QStackedWidget auto focus

QStackedWidget seems to focus and select content of the first child widget when starting or switching page.
This is what I get when starting the program (the timeedit widget is focused and the first digit is selected:
Im removing the focus by setting it to some other uneditable widget using currentChanged event.
Is there a better way to fix this?
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)
self.stackedWidget.setGeometry(QtCore.QRect(110, 160, 351, 231))
self.page_1 = QtWidgets.QWidget()
self.timeEdit = QtWidgets.QTimeEdit(self.page_1)
self.timeEdit.setGeometry(QtCore.QRect(50, 40, 118, 22))
self.stackedWidget.addWidget(self.page_1)
self.page_2 = QtWidgets.QWidget()
self.stackedWidget.addWidget(self.page_2)
MainWindow.setCentralWidget(self.centralwidget)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
# my workaround for removing focus - set focus to the parent window on startup/page change.
MainWindow.setFocus()
self.stackedWidget.currentChanged.connect(self.onPageChange)
def onPageChange(self, index):
MainWindow.setFocus()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

PyQt - Manually resizing menu widget does not resize its content

I am using a QToolButton to open up a menu, which is a simple QWidget. Inside this widget live a QTextEdit and a Button. At the lower right corner there is a QSizeGrip which I want to use to let the user resize the widget and thus the QTextEdit.
If I use this widget on its own inside a MainWindow (Option1) everything works as expected. If however I put this widget into the menu (Option2), I cannot resize it anymore. Dragging the QSizeGrip changes the size of the Menu but not the Widget. I have already experimented with setWindowFlags(QtCore.Qt.SubWindow) and setSizePolicy(..) without any notable effect.
My Question is: How do I make the widget (together with the TextEdit) resizable?
Here is the code and below a picture.
import sys
from PyQt4 import QtGui, QtCore
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
self.TextEdit = QtGui.QTextEdit()
self.Button = QtGui.QPushButton("Push")
self.UpdateWidget = QtGui.QWidget()
self.UpdateWidget.setLayout(QtGui.QHBoxLayout())
self.UpdateWidget.layout().addWidget(self.Button, 1)
self.UpdateWidget.layout().addWidget(QtGui.QSizeGrip(self), 0)
self.layout().addWidget(self.TextEdit)
self.layout().addWidget(self.UpdateWidget)
self.layout().setSpacing(0)
self.layout().setContentsMargins(0,0,0,0)
self.UpdateWidget.layout().setSpacing(4)
self.UpdateWidget.layout().setContentsMargins(0,0,0,0)
# This is what I already tried to make the menu resizable:
#self.setWindowFlags(QtCore.Qt.SubWindow)
#self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
#self.TextEdit.setWindowFlags(QtCore.Qt.SubWindow)
#self.TextEdit.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
class ToolBar(QtGui.QWidget):
def __init__(self, parent=None):
super(ToolBar, self).__init__(parent)
self.setLayout(QtGui.QHBoxLayout())
self.Button = QtGui.QToolButton()
self.Button.setText("Open Text Editor")
self.Button.setPopupMode(QtGui.QToolButton.InstantPopup)
self.Button.setMenu(QtGui.QMenu(self.Button))
action = QtGui.QWidgetAction(self.Button)
action.setDefaultWidget(MyWidget())
self.Button.menu().addAction(action)
self.layout().addWidget(self.Button)
class App(QtGui.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent)
#Option 1:
#Use MyWidget in MainWindow. This works as expected.
#self.central = MyWidget()
#Option 2:
#Use MyWidget as default widgt for a menu action.
#In this case MyWidget cannot be resized.
self.central = ToolBar()
self.setCentralWidget(self.central)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App()
thisapp.show()
sys.exit(app.exec_())
Problem is because Button.menu doesn't inform your widget about size change.
In ToolBar you can assign own function to resizeEvent which will resize your widget.
You need access to your widget
self.MyW = MyWidget()
and you can assign own function
self.Button.menu().resizeEvent = self.onResize
def onResize(self, event):
self.MyW.resize(event.size())
Full code:
import sys
from PyQt4 import QtGui, QtCore
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
self.TextEdit = QtGui.QTextEdit()
self.Button = QtGui.QPushButton("Push")
self.UpdateWidget = QtGui.QWidget()
self.UpdateWidget.setLayout(QtGui.QHBoxLayout())
self.UpdateWidget.layout().addWidget(self.Button, 1)
self.UpdateWidget.layout().addWidget(QtGui.QSizeGrip(self), 0)
self.layout().addWidget(self.TextEdit)
self.layout().addWidget(self.UpdateWidget)
self.layout().setSpacing(0)
self.layout().setContentsMargins(0,0,0,0)
self.UpdateWidget.layout().setSpacing(4)
self.UpdateWidget.layout().setContentsMargins(0,0,0,0)
class ToolBar(QtGui.QWidget):
def __init__(self, parent=None):
super(ToolBar, self).__init__(parent)
self.setLayout(QtGui.QHBoxLayout())
self.Button = QtGui.QToolButton()
self.Button.setText("Open Text Editor")
self.Button.setPopupMode(QtGui.QToolButton.InstantPopup)
self.Button.setMenu(QtGui.QMenu(self.Button))
self.MyW = MyWidget() # <-- here
action = QtGui.QWidgetAction(self.Button)
action.setDefaultWidget(self.MyW) # <-- here
self.Button.menu().addAction(action)
self.layout().addWidget(self.Button)
self.Button.menu().resizeEvent = self.onResize # <-- here
def onResize(self, event): # <-- here
self.MyW.resize(event.size()) # <-- here
class App(QtGui.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent)
#Option 1:
#Use MyWidget in MainWindow. This works as expected.
#self.central = MyWidget()
#Option 2:
#Use MyWidget as default widgt for a menu action.
#In this case MyWidget cannot be resized.
self.central = ToolBar()
self.setCentralWidget(self.central)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App()
thisapp.show()
sys.exit(app.exec_())
Maybe it can be done olny with SizeGrip and MyWidget but I didn't try

PyQt button click area (Non rectangular area)

I was working on a PySide interface for Maya and i was wondering if its possible to define a NON RECTANGULAR clickeable area for a button.
I tried using QPushButton and also extending a QLabel object to get button behavior but do you know if its possible to get a button containing a picture with alpha channel and use that alpha to define the click area for a button?
I'd appreciate a lot if you can guide me through how to solve this problem.
Thanks in advance.
I've tried this...
from PySide import QtCore
from PySide import QtGui
class QLabelButton(QtGui.QLabel):
def __init(self, parent):
QtGui.QLabel.__init__(self, parent)
def mousePressEvent(self, ev):
self.emit(QtCore.SIGNAL('clicked()'))
class CustomButton(QtGui.QWidget):
def __init__(self, parent=None, *args):
super(CustomButton, self).__init__(parent)
self.setMinimumSize(300, 350)
self.setMaximumSize(300, 350)
picture = __file__.replace('qbtn.py', '') + 'mario.png'
self.button = QLabelButton(self)
self.button.setPixmap(QtGui.QPixmap(picture))
self.button.setScaledContents(True)
self.connect(self.button, QtCore.SIGNAL('clicked()'), self.onClick)
def onClick(self):
print('Button was clicked')
if __name__ == '__main__':
app = QApplication(sys.argv)
win = CustomButton()
win.show()
app.exec_()
sys.exit()
mario.png
This is the final code i get to solve my above question...
from PySide import QtCore
from PySide import QtGui
class QLabelButton(QtGui.QLabel):
def __init(self, parent):
QtGui.QLabel.__init__(self, parent)
def mousePressEvent(self, ev):
self.emit(QtCore.SIGNAL('clicked()'))
class CustomButton(QtGui.QWidget):
def __init__(self, parent=None, *args):
super(CustomButton, self).__init__(parent)
self.setMinimumSize(300, 350)
self.setMaximumSize(300, 350)
pixmap = QtGui.QPixmap('D:\mario.png')
self.button = QLabelButton(self)
self.button.setPixmap(pixmap)
self.button.setScaledContents(True)
self.button.setMask(pixmap.mask()) # THIS DOES THE MAGIC
self.connect(self.button, QtCore.SIGNAL('clicked()'), self.onClick)
def onClick(self):
print('Button was clicked')
You can do this by catching the press/release events and checking the position of the click with the value of the pixel in the image to decide if the widget should emit a click or not.
class CustomButton(QWidget):
def __init__(self, parent, image):
super(CustomButton, self).__init__(parent)
self.image = image
def sizeHint(self):
return self.image.size()
def mouseReleaseEvent(self, event):
# Position of click within the button
pos = event.pos()
# Assuming button is the same exact size as image
# get the pixel value of the click point.
pixel = self.image.alphaChannel().pixel(pos)
if pixel:
# Good click, pass the event along, will trigger a clicked signal
super(CustomButton, self).mouseReleaseEvent(event)
else:
# Bad click, ignore the event, no click signal
event.ignore()

Resources