QStatusBar message disappears on menu hover - python

I have a very basic QMainWindow application that contains a menubar and a statusbar. When I hover over the menu the status message disappears. More precisely, the status message is cleared. I have no idea what is causing this behavior but it's resulting in a very difficult workaround for what I hoped to be trivial behavior.
This is problematic for the following reason:
I can make the message permanent by adding a QLabel widget to the QStatusBar, but then I get the awkward border. I don't want the border. The only way I know how to remove the border is via QStatusBar.setStyleSheet(). I am using a palette for my color scheme as opposed to a stylesheet so modifying the stylesheet messes up other colors. I also can't restore the original statusBar QLabel color when I make a modification via the stylesheet. I'm not the best at using stylesheets.
Is there a way to prevent the menu interaction from clearing the status message? If not, is there a way to remove the border from the StatusBar when adding a QLabel widget while preserving my palette (maybe not via stylesheets)?
#!/usr/bin/env python
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class win(QMainWindow):
def __init__(self,parent=None):
super(win,self).__init__(parent)
self.menubar = QMenuBar(self)
self.fileMenu = QMenu("File")
self.exitAction = QAction("Exit",self)
self.fileMenu.addAction(self.exitAction)
self.menubar.addMenu(self.fileMenu)
self.statusBar().showMessage("Hello")
self.connect(self.exitAction,SIGNAL("triggered()"), self.close)
if __name__ == "__main__":
app = QApplication(sys.argv)
GUI = win()
GUI.show()
app.exec_()

I got the same problem, and I found another way which is creating a new QLabel
self.myMessage = QtGui.QLabel()
self.myMessage.setText("Hello")
and add it as an widget to the status bar on the left
self.statusBar.addWidget(self.myMessage)
or on the right
self.statusBar.addPermanentWidget(self.myMessage)

Basically, each widget you hover over sets the status bar text to their statusTip property even when that property is an empty string.
For QMenu, the text is stored in the menuAction action status tip, so, you can have a text instead of just clearing the status bar with something like this:
self.fileMenu.menuAction().setStatusTip("File Menu is hovered")
To prevent anything to change the status bar, you can probably install an eventFilter on the status bar and filter out all QStatusTipEvent.

Just to update Lazywii's answer regarding using a QLabel. That code didn't work exactly as is so maybe there have been some changes since 2016 but what did work in 2020 on PyQt5 is:
self.myMessage = QtWidgets.QLabel()
self.myMessage.setText("My message not affected by tooltips from hovering")
self.statusbar.addWidget(self.myMessage)

One complete example
# Created by BaiJiFeiLong#gmail.com at 2022/2/15 22:27
from PySide2 import QtWidgets, QtCore, QtGui
class StatusTipFilter(QtCore.QObject):
def eventFilter(self, watched: QtCore.QObject, event: QtCore.QEvent) -> bool:
if isinstance(event, QtGui.QStatusTipEvent):
return True
return super().eventFilter(watched, event)
app = QtWidgets.QApplication()
window = QtWidgets.QMainWindow()
window.menuBar().addMenu("File")
window.statusBar().showMessage("Ready")
window.menuBar().installEventFilter(StatusTipFilter(window))
window.show()
app.exec_()

And to answer the portion about removing the border from the statusbar: self.statusbar().setStyleSheet("QStatusBar::item{ border: 0px solid black };") does the trick. It is important to setStyleSheet only on the statusbar object and not the entire application.

Related

PYQT5 set all labels to black

I have several checkboxes which control various procedures, once the code has run all checkboxes which were successful are coloured green e.g. self.cbMarkerDetection.setStyleSheet("color: green;") as it goes to the next job I want to reset the colour back to black. Obviously I could do self.cbMarkerDetection.setStyleSheet("color: black;") for every checkbox... but I'm hoping there is some way to do something like:
for checkbox in allcheckboxes:
self.checkbox.setStyleSheet("color: black;")
A simple coded example, two check boxes, when selected it goes green in colour, when I click the other check box I want all to change back to black, imagine checkboxes a-g, when checkbox (or a button if you like) is selected/checked the selected goes green when checkbox z (or a button) is selected/clicked all the existing check boxes turn black. As stated above, I could list all the checkboxes and set them black, but I'm looking for a quicker way with less code:
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QLabel, QCheckBox, QWidget
from PyQt5.QtCore import QSize
class ExampleWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(140, 40))
self.setWindowTitle("Checkbox")
self.a = QCheckBox("box1",self)
self.a.stateChanged.connect(self.clickBox)
self.a.move(20,20)
self.a.resize(320,40)
self.b = QCheckBox("box2",self)
self.b.stateChanged.connect(self.clickBox)
self.b.move(20,80)
self.b.resize(320,40)
def clickBox(self, state):
if state == QtCore.Qt.Checked:
print('Checked')
self.checkbox.setStyleSheet("color: green;")
else:
print('Unchecked')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWin = ExampleWindow()
mainWin.show()
sys.exit( app.exec_() )
The simplest solution is to use findChildren():
for checkbox in self.findChildren(QCheckBox, options=Qt.FindDirectChildrenOnly):
checkbox.setStyleSheet("color: black;")
If you have other checkboxes in your widget that should not included in the list, you can create an empty subclass and use that for the filter:
class MyCheckBox(QCheckBox): pass
# ...
self.a = MyCheckBox("box1", self)
# etc...
Then you use findChildren with the custom class name:
for checkbox in self.findChildren(MyCheckBox, options=Qt.FindDirectChildrenOnly):
checkbox.setStyleSheet("color: black;")
Note that using fixed geometries is discouraged, and you should prefer layout managers instead.

Python PyQt5 how to show the full QMenuBar with a QWidget

I'm getting this weird result when using QMenuBar I've used this exact code before for the QMenuBar and it worked perfectly. But it doesn't show more than 1 QMenu
This is my code:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
from functools import partial
class MainMenu(QWidget):
def __init__(self, parent = None):
super(MainMenu, self).__init__(parent)
# background = QWidget(self)
lay = QVBoxLayout(self)
lay.setContentsMargins(5, 35, 5, 5)
self.menu()
self.setWindowTitle('Control Panel')
self.setWindowIcon(self.style().standardIcon(getattr(QStyle, 'SP_DialogNoButton')))
self.grid = QGridLayout()
lay.addLayout(self.grid)
self.setLayout(lay)
self.setMinimumSize(400, 320)
def menu(self):
menubar = QMenuBar(self)
viewMenu = menubar.addMenu('View')
viewStatAct = QAction('Dark mode', self, checkable=True)
viewStatAct.setStatusTip('enable/disable Dark mode')
viewMenu.addAction(viewStatAct)
settingsMenu = menubar.addMenu('Configuration')
email = QAction('Set Email', self)
settingsMenu.addAction(email)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainMenu()
main.show()
sys.exit(app.exec_())
Result:
I am aware that I am using QWidget when I should be using QMainWindow But is there a workaround???
(I apologize in advance for the terrible quality of the image, there is no good way to take a picture of a QMenuBar)
The problem is that with a QWidget you are not using the "private" layout that a QMainWindow has, which automatically resizes specific children widgets (including the menubar, the statusbar, the dock widgets, the toolbars and, obviously, the "centralWidget").
Remember that a QMainWindow has its own layout (which can't and shouldn't be changed), because it needs that specific custom layout to lay out the aforementioned widgets. If you want to set a layout for the main window, you'll need to apply it to its centralWidget.
Read carefully how the Main Window Framework behaves; as the documentation reports:
Note: Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.
In order to work around that when using a basic QWidget, you'll have to manually resize the children widgets accordingly. In your case, you only need to resize the menubar, as long as you have a reference to it:
def menu(self):
self.menubar = QMenuBar(self)
# any other function has to be run against the *self.menubar* object
viewMenu = self.menubar.addMenu('View')
# etcetera...
def resizeEvent(self, event):
# calling the base class resizeEvent function is not usually
# required, but it is for certain widgets (especially item views
# or scroll areas), so just call it anyway, just to be sure, as
# it's a good habit to do that for most widget classes
super(MainMenu, self).resizeEvent(event)
# now that we have a direct reference to the menubar widget, we are
# also able to resize it, allowing all actions to be shown (as long
# as they are within the provided size
self.menubar.resize(self.width(), self.menubar.height())
Note: you can also "find" the menubar by means of self.findChild(QtWidgets.QMenuBar) or using the objectName, but using an instance attribute is usually an easier and better solution.
Set minimum width
self.setMinimumSize(320,240)

PyQt5: "No Fill" in Color Dialog?

I am coding a GUI in PyQt5, where I need a color picker.
So far, I use the QColorDialog Class, which works fine for selecting a color - but my problem is that there seems to be no way to select "no color" (or "no fill", like its known from PowerPoint or Adobe Illustrator).
How to achieve to select "no color"? (The documentation only mentions a flag for transparency, but this is not helpful for me...)
If you don't mind using a non-native dialog, it is quite easy to customize it.
Below is a very basic implementation that shows how to embed the existing dialog, and add an extra "No Color" button at the bottom. The rest of the implementation is left as an exercise for the reader...
from PyQt5 import QtCore, QtWidgets
class ColorDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
widget = QtWidgets.QColorDialog()
widget.setWindowFlags(QtCore.Qt.Widget)
widget.setOptions(
QtWidgets.QColorDialog.DontUseNativeDialog |
QtWidgets.QColorDialog.NoButtons)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(widget)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(QtWidgets.QPushButton('No Color'))
hbox.addWidget(QtWidgets.QPushButton('Cancel'))
hbox.addWidget(QtWidgets.QPushButton('Ok'))
layout.addLayout(hbox)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
dialog = ColorDialog()
dialog.show()
sys.exit(app.exec_())

PyQt: ListWidget.insertItem not shown

I have a fairly simply PyQt question. (Python 3.4, PyQt 4.11.3, Qt 4.8.5) I built a very simple dialog using Qt Designer (Ui_Dialog). This object has a QPushButton, a QLineEdit, and a QListWidget. I wrote another object that inherits from Ui_Dialog, and sets up a returnPressed signal from QLineEdit that should add some text to the QListWidget. Unfortunately, this does not work.
Here's my code:
import sys
from PyQt4 import QtGui
from dialog import Ui_Dialog
class ImDialog(QtGui.QDialog, Ui_Dialog):
def __init__(self):
super(ImDialog, self).__init__()
self.setupUi(self)
self.lineEdit.returnPressed.connect(self.additem)
self.pushButton.clicked.connect(self.listWidget.clear)
def additem(self):
text = self.lineEdit.text()
print(text)
self.listWidget.insertItem(0, text)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ui = ImDialog()
ui.show()
sys.exit(app.exec_())
The text in the line editor prints fine to the terminal, but it is not added to the listWidget.
Interestingly, if I comment out the sys.exit line and run this in an IPython terminal, I can add as much text as I like to the listWidget without a problem.
[In 1]: %run that_program.py
[In 2]: ui.listWidget.insertItem(0, "Test") # This works fine
If anyone has any suggestions to get this to work (outside IPython), I would appreciate the help. Thanks
There is only one button in your dialog, and so it will become the auto-default. This means that whenever you press enter in the dialog, the button will receive a press event, even if it doesn't currently have the keyboard focus.
So the item does get added to the list-widget - it's just that it then immediately gets cleared by the auto-default button.
To fix this, reset the auto-default like so:
self.pushButton.setAutoDefault(False)
(NB: you can also change this property in Qt Designer).

QToolbar on mac could not be unified even after setting setUnifiedTitleAndToolBarOnMac flag

This is a Mac OS QT issue,
I have created a QMainWindow and added a toolbar to it.
after setting "setUnifiedTitleAndToolBarOnMac" flag to TRUE my toolbar got unified to my mainwindow.
But when i customize the window buttons such as only close button option,
my window Toolbar got detached from title bar as if it looks like a winodow OS toolbar
not like a Mac native one.
Attached my test code below which is in pyqt.
Hope someone know how to achieve it.
Thanks
from PyQt4 import QtGui
from PyQt4 import QtCore
class Ui_windo(QtGui.QMainWindow):
def __init__(self,parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setObjectName("windo")
self.resize(400, 300)
self.setWindowTitle(QtGui.QApplication.translate("window", "window", None, QtGui.QApplication.UnicodeUTF8))
self.b1 = QtGui.QToolButton()
self.b1.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly)
self.b1.setText('Test')
self.b1.setObjectName("b1")
self.b1.setCheckable(True)
_toolBar = self.addToolBar('test')
_toolBar.setMovable(False)
_toolBar.addWidget(self.b1)
self.setUnifiedTitleAndToolBarOnMac(True);
_windowButtons = QtCore.Qt.Window| QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.CustomizeWindowHint
self.setWindowFlags(_windowButtons)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
ui = Ui_windo()
ui.show()
sys.exit(app.exec_())
Seems that the Qt.CustomizeWindowHint flag removes the styling from the toolbar. Not sure if this is expected behavior or a bug. There have been a number of style-related bug reports pertaining to setUnifiedTitleAndToolBarOnMac. You should post it to confirm whether its expected or not.
https://bugreports.qt-project.org/secure/IssueNavigator.jspa
If you set your window flag to use Qt.Tool, that will at least get you close by removing the minimize button.

Categories

Resources