Add titlebar customization issues and workarounds.

Add QMdiSubWindow titlebar customization issues, including title bar
icons, to ISSUES.md, and add a working example of a custom title bar.
This is a full-featured, drop-in replacement that could also be modified
for use in a QMainWindow or QDialog settings.

Closes #63.
main
Alex Huszagh 2022-05-07 01:59:55 -05:00
parent aa6a420bdc
commit 4698e92df3
11 changed files with 8249 additions and 6125 deletions

View File

@ -7,6 +7,8 @@ There are limitations to what can be styled with stylesheets, as well as rare bu
- [Menu Hover Background Color](#menu-hover-background-color) - [Menu Hover Background Color](#menu-hover-background-color)
- [QDial](#qdial) - [QDial](#qdial)
- [Custom Style](#custom-style) - [Custom Style](#custom-style)
- [QMdiSubwindow](#qmdisubwindow)
- [Title Bar Icons](#title-bar-icons)
- [QSlider](#qslider) - [QSlider](#qslider)
- [Invisible Ticks](#invisible-ticks) - [Invisible Ticks](#invisible-ticks)
- [QTabBar](#qtabbar) - [QTabBar](#qtabbar)
@ -22,6 +24,8 @@ There are limitations to what can be styled with stylesheets, as well as rare bu
- [Tooltip Colors](#tooltip-colors) - [Tooltip Colors](#tooltip-colors)
- [QWidget](#qwidget) - [QWidget](#qwidget)
- [Standard Icons](#standard-icons) - [Standard Icons](#standard-icons)
- [QWindow](#qwindow)
- [Title Bar Customization](#title-bar-customization)
- [QWizard](#qwizard) - [QWizard](#qwizard)
- [Aero Style Background Color](#aero-style-background-color) - [Aero Style Background Color](#aero-style-background-color)
@ -37,6 +41,12 @@ There are limitations to what can be styled with stylesheets, as well as rare bu
`QDial` cannot be customized via a stylesheet, which is a known bug in [QTBUG-1160](https://bugreports.qt.io/browse/QTBUG-1160). An example of how to style a `QDial` is available in [dial.py](/example/dial.py). This works out-of-the-box, and can be a drop-in replacement for `QDial`. `QDial` cannot be customized via a stylesheet, which is a known bug in [QTBUG-1160](https://bugreports.qt.io/browse/QTBUG-1160). An example of how to style a `QDial` is available in [dial.py](/example/dial.py). This works out-of-the-box, and can be a drop-in replacement for `QDial`.
# QMdiSubwindow
### Title Bar Icons
The tilebar icons (except for the menu icon) cannot be overridden in the stylesheet, which is a known bug in [QTBUG-1399](https://bugreports.qt.io/browse/QTBUG-1399). This bug has been present for ~15 years, so it is unlikely to be patched soon, if ever. For a working example on how to customize your own title bar, including icons, see [Title Bar Customization for QWindow](#title-bar-customization).
# QSlider # QSlider
### Invisible Ticks ### Invisible Ticks
@ -283,7 +293,7 @@ def main():
### Standard Icons ### Standard Icons
Certain standard icons cannot be overwritten in the stylesheet, and therefore a custom style must be installed in the Qt application. The `standard-icons` [extension](/extension/README.md#standard-icons) comes with a set of custom standard icons, and the [standard_icons.py](/example/standard_icons.py) example shows a complete application for how to override the default standard icons. Certain standard icons cannot be overridden in the stylesheet, and therefore a custom style must be installed in the Qt application. The `standard-icons` [extension](/extension/README.md#standard-icons) comes with a set of custom standard icons, and the [standard_icons.py](/example/standard_icons.py) example shows a complete application for how to override the default standard icons.
A simple example of overriding the command link icon for a PyQt6 application is as follows. First, configure with the `standard-icons` extension. A simple example of overriding the command link icon for a PyQt6 application is as follows. First, configure with the `standard-icons` extension.
@ -362,6 +372,14 @@ def main():
return app.exec() return app.exec()
``` ```
# QWindow
### Title Bar Customization
The system title bar cannot be customized extensively, since it depends on either the application style or the system theme for how it renders. For a comprehensive example on how to create your own, custom title bar, with fully functional minimize, maximize, shade, unshade, context help, keep above, window title, and a context menu, see [titlebar.py](/example/titlebar.py). This is a drop-in replacement for the title bar on `QMdiSubWindow` which also lets you customize the placement of where the windows minimize to, but could also be modified for `QMainWindow` or `QDialog`.
![Custom Titlebar](/assets/custom-titlebar.png)
# QWizard # QWizard
### Aero Style Background Color ### Aero Style Background Color

BIN
assets/custom-titlebar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because it is too large Load Diff

View File

@ -2436,3 +2436,59 @@ QMessageBox QPushButton
min-height: 1.1em; min-height: 1.1em;
min-width: 5em; min-width: 5em;
} }
/**
* Special rules for creating a custom titlebar. This can only work
* when setting the Qt property `isTitlebar` to `true`.
*/
QWidget[isTitlebar="true"],
QWidget[isTitlebar="true"] *
{
background-color: #2c3034;
}
/**
* Special rules for creating a border around a top-level frame of a window.
* This can only work when setting the Qt property `isWindow` to `true`.
* We've manually enumerated border widths from 1-5 below.
*/
QFrame[isWindow="true"],
QFrame[frameShape][isWindow="true"]
{
border: 0px transparent #2c3034;
}
QFrame[isWindow="true"][windowFrame="1"],
QFrame[frameShape][isWindow="true"][windowFrame="1"]
{
border: 1px solid #2c3034;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="2"],
QFrame[frameShape][isWindow="true"][windowFrame="2"]
{
border: 2px solid #2c3034;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="3"],
QFrame[frameShape][isWindow="true"][windowFrame="3"]
{
border: 3px solid #2c3034;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="4"],
QFrame[frameShape][isWindow="true"][windowFrame="4"]
{
border: 4px solid #2c3034;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="5"],
QFrame[frameShape][isWindow="true"][windowFrame="5"]
{
border: 5px solid #2c3034;
border-radius: 3px;
}

View File

@ -2436,3 +2436,59 @@ QMessageBox QPushButton
min-height: 1.1em; min-height: 1.1em;
min-width: 5em; min-width: 5em;
} }
/**
* Special rules for creating a custom titlebar. This can only work
* when setting the Qt property `isTitlebar` to `true`.
*/
QWidget[isTitlebar="true"],
QWidget[isTitlebar="true"] *
{
background-color: #d9d8d7;
}
/**
* Special rules for creating a border around a top-level frame of a window.
* This can only work when setting the Qt property `isWindow` to `true`.
* We've manually enumerated border widths from 1-5 below.
*/
QFrame[isWindow="true"],
QFrame[frameShape][isWindow="true"]
{
border: 0px transparent #d9d8d7;
}
QFrame[isWindow="true"][windowFrame="1"],
QFrame[frameShape][isWindow="true"][windowFrame="1"]
{
border: 1px solid #d9d8d7;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="2"],
QFrame[frameShape][isWindow="true"][windowFrame="2"]
{
border: 2px solid #d9d8d7;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="3"],
QFrame[frameShape][isWindow="true"][windowFrame="3"]
{
border: 3px solid #d9d8d7;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="4"],
QFrame[frameShape][isWindow="true"][windowFrame="4"]
{
border: 4px solid #d9d8d7;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="5"],
QFrame[frameShape][isWindow="true"][windowFrame="5"]
{
border: 5px solid #d9d8d7;
border-radius: 3px;
}

View File

@ -2436,3 +2436,59 @@ QMessageBox QPushButton
min-height: 1.1em; min-height: 1.1em;
min-width: 5em; min-width: 5em;
} }
/**
* Special rules for creating a custom titlebar. This can only work
* when setting the Qt property `isTitlebar` to `true`.
*/
QWidget[isTitlebar="true"],
QWidget[isTitlebar="true"] *
{
background-color: #2c3034;
}
/**
* Special rules for creating a border around a top-level frame of a window.
* This can only work when setting the Qt property `isWindow` to `true`.
* We've manually enumerated border widths from 1-5 below.
*/
QFrame[isWindow="true"],
QFrame[frameShape][isWindow="true"]
{
border: 0px transparent #2c3034;
}
QFrame[isWindow="true"][windowFrame="1"],
QFrame[frameShape][isWindow="true"][windowFrame="1"]
{
border: 1px solid #2c3034;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="2"],
QFrame[frameShape][isWindow="true"][windowFrame="2"]
{
border: 2px solid #2c3034;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="3"],
QFrame[frameShape][isWindow="true"][windowFrame="3"]
{
border: 3px solid #2c3034;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="4"],
QFrame[frameShape][isWindow="true"][windowFrame="4"]
{
border: 4px solid #2c3034;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="5"],
QFrame[frameShape][isWindow="true"][windowFrame="5"]
{
border: 5px solid #2c3034;
border-radius: 3px;
}

View File

@ -2436,3 +2436,59 @@ QMessageBox QPushButton
min-height: 1.1em; min-height: 1.1em;
min-width: 5em; min-width: 5em;
} }
/**
* Special rules for creating a custom titlebar. This can only work
* when setting the Qt property `isTitlebar` to `true`.
*/
QWidget[isTitlebar="true"],
QWidget[isTitlebar="true"] *
{
background-color: #d9d8d7;
}
/**
* Special rules for creating a border around a top-level frame of a window.
* This can only work when setting the Qt property `isWindow` to `true`.
* We've manually enumerated border widths from 1-5 below.
*/
QFrame[isWindow="true"],
QFrame[frameShape][isWindow="true"]
{
border: 0px transparent #d9d8d7;
}
QFrame[isWindow="true"][windowFrame="1"],
QFrame[frameShape][isWindow="true"][windowFrame="1"]
{
border: 1px solid #d9d8d7;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="2"],
QFrame[frameShape][isWindow="true"][windowFrame="2"]
{
border: 2px solid #d9d8d7;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="3"],
QFrame[frameShape][isWindow="true"][windowFrame="3"]
{
border: 3px solid #d9d8d7;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="4"],
QFrame[frameShape][isWindow="true"][windowFrame="4"]
{
border: 4px solid #d9d8d7;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="5"],
QFrame[frameShape][isWindow="true"][windowFrame="5"]
{
border: 5px solid #d9d8d7;
border-radius: 3px;
}

View File

@ -166,6 +166,14 @@ def get_compat_definitions(args):
ns.InputMode = QtWidgets.QInputDialog.InputMode ns.InputMode = QtWidgets.QInputDialog.InputMode
ns.RubberBandShape = QtWidgets.QRubberBand.Shape ns.RubberBandShape = QtWidgets.QRubberBand.Shape
ns.TextInteractionFlag = QtCore.Qt.TextInteractionFlag ns.TextInteractionFlag = QtCore.Qt.TextInteractionFlag
ns.WindowType = QtCore.Qt.WindowType
ns.WindowState = QtCore.Qt.WindowState
ns.WidgetAttribute = QtCore.Qt.WidgetAttribute
ns.TextElideMode = QtCore.Qt.TextElideMode
ns.CursorShape = QtCore.Qt.CursorShape
ns.MouseButton = QtCore.Qt.MouseButton
ns.SizePolicy = QtWidgets.QSizePolicy.Policy
ns.SizeConstraint = QtWidgets.QLayout.SizeConstraint
# QObjects # QObjects
ns.QAction = QtGui.QAction ns.QAction = QtGui.QAction
@ -217,7 +225,6 @@ def get_compat_definitions(args):
ns.NoEcho = ns.EchoMode.NoEcho ns.NoEcho = ns.EchoMode.NoEcho
ns.Password = ns.EchoMode.Password ns.Password = ns.EchoMode.Password
ns.PasswordEchoOnEdit = ns.EchoMode.PasswordEchoOnEdit ns.PasswordEchoOnEdit = ns.EchoMode.PasswordEchoOnEdit
ns.WindowMaximized = ns.WindowState.WindowMaximized
ns.SolidLine = ns.PenStyle.SolidLine ns.SolidLine = ns.PenStyle.SolidLine
ns.DotLine = ns.PenStyle.DotLine ns.DotLine = ns.PenStyle.DotLine
ns.FlatCap = ns.PenCapStyle.FlatCap ns.FlatCap = ns.PenCapStyle.FlatCap
@ -229,9 +236,15 @@ def get_compat_definitions(args):
ns.SvgMiterJoin = ns.PenJoinStyle.SvgMiterJoin ns.SvgMiterJoin = ns.PenJoinStyle.SvgMiterJoin
ns.State_HasFocus = ns.StateFlag.State_HasFocus ns.State_HasFocus = ns.StateFlag.State_HasFocus
ns.State_Selected = ns.StateFlag.State_Selected ns.State_Selected = ns.StateFlag.State_Selected
ns.Enter = ns.EventType.Enter
ns.Leave = ns.EventType.Leave
ns.HoverEnter = ns.EventType.HoverEnter ns.HoverEnter = ns.EventType.HoverEnter
ns.HoverMove = ns.EventType.HoverMove ns.HoverMove = ns.EventType.HoverMove
ns.HoverLeave = ns.EventType.HoverLeave ns.HoverLeave = ns.EventType.HoverLeave
ns.MouseButtonPress = ns.EventType.MouseButtonPress
ns.MouseButtonRelease = ns.EventType.MouseButtonRelease
ns.MouseMove = ns.EventType.MouseMove
ns.WindowPalette = ns.ColorRole.Window
ns.PlaceholderText = ns.ColorRole.PlaceholderText ns.PlaceholderText = ns.ColorRole.PlaceholderText
ns.ToolTipBase = ns.ColorRole.ToolTipBase ns.ToolTipBase = ns.ColorRole.ToolTipBase
ns.ToolTipText = ns.ColorRole.ToolTipText ns.ToolTipText = ns.ColorRole.ToolTipText
@ -400,6 +413,40 @@ def get_compat_definitions(args):
ns.RubberBandRectangle = ns.RubberBandShape.Rectangle ns.RubberBandRectangle = ns.RubberBandShape.Rectangle
ns.TextSelectableByMouse = ns.TextInteractionFlag.TextSelectableByMouse ns.TextSelectableByMouse = ns.TextInteractionFlag.TextSelectableByMouse
ns.TextEditorInteraction = ns.TextInteractionFlag.TextEditorInteraction ns.TextEditorInteraction = ns.TextInteractionFlag.TextEditorInteraction
ns.Window = ns.WindowType.Window
ns.Dialog = ns.WindowType.Dialog
ns.SubWindow = ns.WindowType.SubWindow
ns.WindowContextHelpButtonHint = ns.WindowType.WindowContextHelpButtonHint
ns.WindowShadeButtonHint = ns.WindowType.WindowShadeButtonHint
ns.FramelessWindowHint = ns.WindowType.FramelessWindowHint
ns.WindowStaysOnTopHint = ns.WindowType.WindowStaysOnTopHint
ns.WindowNoState = ns.WindowState.WindowNoState
ns.WindowMinimized = ns.WindowState.WindowMinimized
ns.WindowMaximized = ns.WindowState.WindowMaximized
ns.WA_Hover = ns.WidgetAttribute.WA_Hover
ns.WA_LayoutOnEntireRect = ns.WidgetAttribute.WA_LayoutOnEntireRect
ns.WA_LayoutUsesWidgetRect = ns.WidgetAttribute.WA_LayoutUsesWidgetRect
ns.ElideLeft = ns.TextElideMode.ElideLeft
ns.ElideRight = ns.TextElideMode.ElideRight
ns.ElideMiddle = ns.TextElideMode.ElideMiddle
ns.ElideNone = ns.TextElideMode.ElideNone
ns.CrossCursor = ns.CursorShape.CrossCursor
ns.SizeVerCursor = ns.CursorShape.SizeVerCursor
ns.SizeHorCursor = ns.CursorShape.SizeHorCursor
ns.SizeBDiagCursor = ns.CursorShape.SizeBDiagCursor
ns.SizeFDiagCursor = ns.CursorShape.SizeFDiagCursor
ns.SizeAllCursor = ns.CursorShape.SizeAllCursor
ns.WhatsThisCursor = ns.CursorShape.WhatsThisCursor
ns.LeftButton = ns.MouseButton.LeftButton
ns.RightButton = ns.MouseButton.RightButton
ns.SizeFixed = ns.SizePolicy.Fixed
ns.SizeMinimum = ns.SizePolicy.Minimum
ns.SizeMaximum = ns.SizePolicy.Maximum
ns.SizePreferred = ns.SizePolicy.Preferred
ns.SizeExpanding = ns.SizePolicy.Expanding
ns.SizeMinimumExpanding = ns.SizePolicy.MinimumExpanding
ns.SizeIgnored = ns.SizePolicy.Ignored
ns.SetFixedSize = ns.SizeConstraint.SetFixedSize
else: else:
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -459,7 +506,6 @@ def get_compat_definitions(args):
ns.NoEcho = QtWidgets.QLineEdit.NoEcho ns.NoEcho = QtWidgets.QLineEdit.NoEcho
ns.Password = QtWidgets.QLineEdit.Password ns.Password = QtWidgets.QLineEdit.Password
ns.PasswordEchoOnEdit = QtWidgets.QLineEdit.PasswordEchoOnEdit ns.PasswordEchoOnEdit = QtWidgets.QLineEdit.PasswordEchoOnEdit
ns.WindowMaximized = QtCore.Qt.WindowMaximized
ns.SolidLine = QtCore.Qt.SolidLine ns.SolidLine = QtCore.Qt.SolidLine
ns.DotLine = QtCore.Qt.DotLine ns.DotLine = QtCore.Qt.DotLine
ns.FlatCap = QtCore.Qt.FlatCap ns.FlatCap = QtCore.Qt.FlatCap
@ -471,9 +517,15 @@ def get_compat_definitions(args):
ns.SvgMiterJoin = QtCore.Qt.SvgMiterJoin ns.SvgMiterJoin = QtCore.Qt.SvgMiterJoin
ns.State_HasFocus = QtWidgets.QStyle.State_HasFocus ns.State_HasFocus = QtWidgets.QStyle.State_HasFocus
ns.State_Selected = QtWidgets.QStyle.State_Selected ns.State_Selected = QtWidgets.QStyle.State_Selected
ns.Enter = QtCore.QEvent.Enter
ns.Leave = QtCore.QEvent.Leave
ns.HoverEnter = QtCore.QEvent.HoverEnter ns.HoverEnter = QtCore.QEvent.HoverEnter
ns.HoverMove = QtCore.QEvent.HoverMove ns.HoverMove = QtCore.QEvent.HoverMove
ns.HoverLeave = QtCore.QEvent.HoverLeave ns.HoverLeave = QtCore.QEvent.HoverLeave
ns.MouseButtonPress = QtCore.QEvent.MouseButtonPress
ns.MouseButtonRelease = QtCore.QEvent.MouseButtonRelease
ns.MouseMove = QtCore.QEvent.MouseMove
ns.WindowPalette = QtGui.QPalette.Window
ns.PlaceholderText = QtGui.QPalette.PlaceholderText ns.PlaceholderText = QtGui.QPalette.PlaceholderText
ns.ToolTipBase = QtGui.QPalette.ToolTipBase ns.ToolTipBase = QtGui.QPalette.ToolTipBase
ns.ToolTipText = QtGui.QPalette.ToolTipText ns.ToolTipText = QtGui.QPalette.ToolTipText
@ -636,6 +688,40 @@ def get_compat_definitions(args):
ns.RubberBandRectangle = QtWidgets.QRubberBand.Rectangle ns.RubberBandRectangle = QtWidgets.QRubberBand.Rectangle
ns.TextSelectableByMouse = QtCore.Qt.TextSelectableByMouse ns.TextSelectableByMouse = QtCore.Qt.TextSelectableByMouse
ns.TextEditorInteraction = QtCore.Qt.TextEditorInteraction ns.TextEditorInteraction = QtCore.Qt.TextEditorInteraction
ns.Window = QtCore.Qt.Window
ns.Dialog = QtCore.Qt.Dialog
ns.SubWindow = QtCore.Qt.SubWindow
ns.WindowContextHelpButtonHint = QtCore.Qt.WindowContextHelpButtonHint
ns.WindowShadeButtonHint = QtCore.Qt.WindowShadeButtonHint
ns.FramelessWindowHint = QtCore.Qt.FramelessWindowHint
ns.WindowStaysOnTopHint = QtCore.Qt.WindowStaysOnTopHint
ns.WindowNoState = QtCore.Qt.WindowNoState
ns.WindowMinimized = QtCore.Qt.WindowMinimized
ns.WindowMaximized = QtCore.Qt.WindowMaximized
ns.WA_Hover = QtCore.Qt.WA_Hover
ns.WA_LayoutOnEntireRect = QtCore.Qt.WA_LayoutOnEntireRect
ns.WA_LayoutUsesWidgetRect = QtCore.Qt.WA_LayoutUsesWidgetRect
ns.ElideLeft = QtCore.Qt.ElideLeft
ns.ElideRight = QtCore.Qt.ElideRight
ns.ElideMiddle = QtCore.Qt.ElideMiddle
ns.ElideNone = QtCore.Qt.ElideNone
ns.CrossCursor = QtCore.Qt.CrossCursor
ns.SizeVerCursor = QtCore.Qt.SizeVerCursor
ns.SizeHorCursor = QtCore.Qt.SizeHorCursor
ns.SizeBDiagCursor = QtCore.Qt.SizeBDiagCursor
ns.SizeFDiagCursor = QtCore.Qt.SizeFDiagCursor
ns.SizeAllCursor = QtCore.Qt.SizeAllCursor
ns.WhatsThisCursor = QtCore.Qt.WhatsThisCursor
ns.LeftButton = QtCore.Qt.LeftButton
ns.RightButton = QtCore.Qt.RightButton
ns.SizeFixed = QtWidgets.QSizePolicy.Fixed
ns.SizeMinimum = QtWidgets.QSizePolicy.Minimum
ns.SizeMaximum = QtWidgets.QSizePolicy.Maximum
ns.SizePreferred = QtWidgets.QSizePolicy.Preferred
ns.SizeExpanding = QtWidgets.QSizePolicy.Expanding
ns.SizeMinimumExpanding = QtWidgets.QSizePolicy.MinimumExpanding
ns.SizeIgnored = QtWidgets.QSizePolicy.Ignored
ns.SetFixedSize = QtWidgets.QLayout.SetFixedSize
return ns return ns
@ -643,12 +729,19 @@ def get_colors(args, compat):
'''Create shared colors dependent on the stylesheet.''' '''Create shared colors dependent on the stylesheet.'''
ns = argparse.Namespace() ns = argparse.Namespace()
ns.Background = compat.QtGui.QColor(255, 255, 0)
ns.Foreground = compat.QtGui.QColor(0, 255, 255)
ns.Selected = compat.QtGui.QColor(61, 174, 233) ns.Selected = compat.QtGui.QColor(61, 174, 233)
ns.PlaceholderColor = compat.QtGui.QColor(255, 0, 0) ns.PlaceholderColor = compat.QtGui.QColor(255, 0, 0)
ns.TickColor = compat.QtGui.QColor(255, 0, 0) ns.TickColor = compat.QtGui.QColor(255, 0, 0)
ns.ToolTipBase = compat.QtGui.QColor(0, 255, 0) ns.ToolTipBase = compat.QtGui.QColor(0, 255, 0)
ns.ToolTipText = compat.QtGui.QColor(0, 0, 255) ns.ToolTipText = compat.QtGui.QColor(0, 0, 255)
ns.MidTone = compat.QtGui.QColor(127, 127, 127)
ns.ViewBackground = compat.QtGui.QColor(0, 0, 0)
ns.TabBackground = compat.QtGui.QColor(0, 0, 0)
if 'dark' in args.stylesheet: if 'dark' in args.stylesheet:
ns.Background = compat.QtGui.QColor(49, 54, 59)
ns.Foreground = compat.QtGui.QColor(239, 240, 241)
ns.GrooveBackground = compat.QtGui.QColor(98, 101, 104) ns.GrooveBackground = compat.QtGui.QColor(98, 101, 104)
ns.GrooveBorder = compat.QtGui.QColor(49, 54, 59) ns.GrooveBorder = compat.QtGui.QColor(49, 54, 59)
ns.HandleBackground = compat.QtGui.QColor(29, 32, 35) ns.HandleBackground = compat.QtGui.QColor(29, 32, 35)
@ -658,7 +751,12 @@ def get_colors(args, compat):
ns.TickColor = compat.QtGui.QColor(51, 78, 94) ns.TickColor = compat.QtGui.QColor(51, 78, 94)
ns.ToolTipBase = compat.QtGui.QColor(49, 54, 59) ns.ToolTipBase = compat.QtGui.QColor(49, 54, 59)
ns.ToolTipText = compat.QtGui.QColor(239, 240, 241) ns.ToolTipText = compat.QtGui.QColor(239, 240, 241)
ns.MidTone = compat.QtGui.QColor(118, 121, 124)
ns.ViewBackground = compat.QtGui.QColor(29, 32, 35)
ns.TabBackground = compat.QtGui.QColor(44, 48, 52)
elif 'light' in args.stylesheet: elif 'light' in args.stylesheet:
ns.Background = compat.QtGui.QColor(239, 240, 241)
ns.Foreground = compat.QtGui.QColor(49, 54, 59)
ns.GrooveBackground = compat.QtGui.QColor(106, 105, 105, 179) ns.GrooveBackground = compat.QtGui.QColor(106, 105, 105, 179)
ns.GrooveBorder = compat.QtGui.QColor(239, 240, 241) ns.GrooveBorder = compat.QtGui.QColor(239, 240, 241)
ns.HandleBackground = compat.QtGui.QColor(239, 240, 241) ns.HandleBackground = compat.QtGui.QColor(239, 240, 241)
@ -668,6 +766,9 @@ def get_colors(args, compat):
ns.TickColor = compat.QtGui.QColor(61, 173, 232, 51) ns.TickColor = compat.QtGui.QColor(61, 173, 232, 51)
ns.ToolTipBase = compat.QtGui.QColor(49, 54, 59) ns.ToolTipBase = compat.QtGui.QColor(49, 54, 59)
ns.ToolTipText = compat.QtGui.QColor(239, 240, 241) ns.ToolTipText = compat.QtGui.QColor(239, 240, 241)
ns.MidTone = compat.QtGui.QColor(186, 185, 184)
ns.ViewBackground = compat.QtGui.QColor(239, 240, 241)
ns.TabBackground = compat.QtGui.QColor(217, 216, 215)
return ns return ns
@ -759,7 +860,7 @@ def get_icon_map(args, compat):
return icon_map return icon_map
def setup_app(args, unknown, compat, style_class=None): def setup_app(args, unknown, compat, style_class=None, window_class=None):
'''Setup code for the Qt application.''' '''Setup code for the Qt application.'''
if args.scale != 1: if args.scale != 1:
@ -774,7 +875,9 @@ def setup_app(args, unknown, compat, style_class=None):
style = style_class(style) style = style_class(style)
app.setStyle(style) app.setStyle(style)
window = compat.QtWidgets.QMainWindow() if window_class is None:
window_class = compat.QtWidgets.QMainWindow
window = window_class()
# use the default font size # use the default font size
font = app.font() font = app.font()
@ -803,12 +906,28 @@ def exec_app(args, app, window, compat):
window.show() window.show()
return execute(args, app) return execute(args, app)
def execute(args, widget): def execute(args, widget, *params):
'''Shared code to call `exec()` on a widget.''' '''Shared code to call `exec()` on a widget.'''
if args.pyqt6: if args.pyqt6:
return widget.exec() return widget.exec(*params)
return widget.exec_() return widget.exec_(*params)
def single_point_position(args, event):
'''Shared code to call `pos()` on a single-point event.'''
if args.pyqt6:
# Qt6 returns `QPointF`, which is overkill.
return event.position().toPoint()
return event.pos()
def single_point_global_position(args, event):
'''Shared code to call `globalPos()` on a single-point event.'''
if args.pyqt6:
# Qt6 returns `QPointF`, which is overkill.
return event.globalPosition().toPoint()
return event.globalPos()
def native_icon(style, icon, option=None, widget=None): def native_icon(style, icon, option=None, widget=None):
'''Get a standard icon for the native style''' '''Get a standard icon for the native style'''

1651
example/titlebar.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2436,3 +2436,59 @@ QMessageBox QPushButton
min-height: 1.1em; min-height: 1.1em;
min-width: 5em; min-width: 5em;
} }
/**
* Special rules for creating a custom titlebar. This can only work
* when setting the Qt property `isTitlebar` to `true`.
*/
QWidget[isTitlebar="true"],
QWidget[isTitlebar="true"] *
{
background-color: ^tab:background^;
}
/**
* Special rules for creating a border around a top-level frame of a window.
* This can only work when setting the Qt property `isWindow` to `true`.
* We've manually enumerated border widths from 1-5 below.
*/
QFrame[isWindow="true"],
QFrame[frameShape][isWindow="true"]
{
border: 0px transparent ^tab:background^;
}
QFrame[isWindow="true"][windowFrame="1"],
QFrame[frameShape][isWindow="true"][windowFrame="1"]
{
border: 1px solid ^tab:background^;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="2"],
QFrame[frameShape][isWindow="true"][windowFrame="2"]
{
border: 2px solid ^tab:background^;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="3"],
QFrame[frameShape][isWindow="true"][windowFrame="3"]
{
border: 3px solid ^tab:background^;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="4"],
QFrame[frameShape][isWindow="true"][windowFrame="4"]
{
border: 4px solid ^tab:background^;
border-radius: 3px;
}
QFrame[isWindow="true"][windowFrame="5"],
QFrame[frameShape][isWindow="true"][windowFrame="5"]
{
border: 5px solid ^tab:background^;
border-radius: 3px;
}

View File

@ -136,21 +136,25 @@ def splash_timer(splash, window):
splash.finish(window) splash.finish(window)
window.show() window.show()
def standard_icon(widget, icon):
'''Get a standard icon depending on the stylesheet.'''
return shared.standard_icon(args, widget, icon, ICON_MAP)
def close_icon(widget): def close_icon(widget):
'''Get the close icon depending on the stylesheet.''' '''Get the close icon depending on the stylesheet.'''
return shared.standard_icon(args, widget, compat.SP_DockWidgetCloseButton, ICON_MAP) return standard_icon(widget, compat.SP_DockWidgetCloseButton)
def reset_icon(widget): def reset_icon(widget):
'''Get the reset icon depending on the stylesheet.''' '''Get the reset icon depending on the stylesheet.'''
return shared.standard_icon(args, widget, compat.SP_DialogResetButton, ICON_MAP) return standard_icon(widget, compat.SP_DialogResetButton)
def next_icon(widget): def next_icon(widget):
'''Get the next icon depending on the stylesheet.''' '''Get the next icon depending on the stylesheet.'''
return shared.standard_icon(args, widget, compat.SP_ArrowRight, ICON_MAP) return standard_icon(widget, compat.SP_ArrowRight)
def previous_icon(widget): def previous_icon(widget):
'''Get the previous icon depending on the stylesheet.''' '''Get the previous icon depending on the stylesheet.'''
return shared.standard_icon(args, widget, compat.SP_ArrowLeft, ICON_MAP) return standard_icon(widget, compat.SP_ArrowLeft)
def test_progressbar_horizontal(widget, *_): def test_progressbar_horizontal(widget, *_):
child = [] child = []
@ -409,7 +413,30 @@ def test_tooltips_menu(widget, window, font, width, *_):
def test_mdi_area(widget, *_): def test_mdi_area(widget, *_):
child = QtWidgets.QMdiArea(widget) child = QtWidgets.QMdiArea(widget)
child.addSubWindow(QtWidgets.QMdiSubWindow()) child.addSubWindow(QtWidgets.QMdiSubWindow())
child.addSubWindow(QtWidgets.QMdiSubWindow()) window = QtWidgets.QMdiSubWindow()
flags = window.windowFlags()
flags |= compat.WindowContextHelpButtonHint
flags |= compat.WindowShadeButtonHint
window.setWindowFlags(flags)
window.setWindowTitle('Subwindow')
child.addSubWindow(window)
return child
def test_partial_mdi_area(widget, *_):
child = [
QtWidgets.QWidget(),
QtWidgets.QMdiArea(widget),
]
child[0].setMinimumSize(200, 200)
child[1].addSubWindow(QtWidgets.QMdiSubWindow())
window = QtWidgets.QMdiSubWindow()
flags = window.windowFlags()
flags |= compat.WindowContextHelpButtonHint
flags |= compat.WindowShadeButtonHint
window.setWindowFlags(flags)
window.setWindowTitle('Subwindow')
child[1].addSubWindow(window)
return child return child
@ -1200,7 +1227,7 @@ def test_view_scrollarea(widget, *_):
child.setRowCount(100) child.setRowCount(100)
for index in range(100): for index in range(100):
row = QtWidgets.QTableWidgetItem(f'Row {index + 1}') row = QtWidgets.QTableWidgetItem(f'Row {index + 1}')
child.setVerticalHeaderItem(0, row) child.setVerticalHeaderItem(index, row)
column = QtWidgets.QTableWidgetItem(f'Column {index + 1}') column = QtWidgets.QTableWidgetItem(f'Column {index + 1}')
child.setHorizontalHeaderItem(index, column) child.setHorizontalHeaderItem(index, column)