lets consider the following screenshot:
You can see that the top toolbar displays 2 rows; however to do so , in need to click on the >> at the top right (circled in red) and keep hovering hover the toolbar area, which can get a bit annoying.
Is there a way to keep the 2 rows of the toolbar always displaying?
The solution is:
Expand the QToolBar using the layout that in the implementation of the private API has a slot called setExpanded() that allows to expand the QToolBar.
Hide the button, and for this case it only worked to set the size to QSize(0, 0).
Deactivate the event Leave of the QToolBar so that it does not collapse.
from PyQt5 import QtCore, QtGui, QtWidgets
class ToolBar(QtWidgets.QToolBar):
def __init__(self, parent=None):
super().__init__(parent)
lay = self.findChild(QtWidgets.QLayout)
if lay is not None:
lay.setExpanded(True)
QtCore.QTimer.singleShot(0, self.on_timeout)
#QtCore.pyqtSlot()
def on_timeout(self):
button = self.findChild(QtWidgets.QToolButton, "qt_toolbar_ext_button")
if button is not None:
button.setFixedSize(0, 0)
def event(self, e):
if e.type() == QtCore.QEvent.Leave:
return True
return super().event(e)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QMainWindow()
toolbar = ToolBar()
for i in range(20):
toolbar.addAction("action{}".format(i))
w.addToolBar(QtCore.Qt.TopToolBarArea, toolbar)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Related
I am trying to build a hover Dialog but i am stuck at the interaction between QComboBox selection and QDialog's leaveEvent. it looks like when i try to click on combobox to select something, it triggers a leaveEvent which then hides my QDialog. Why is this happening? What can i try to ensure that the Dialog is only hidden when I move my mouse outside of the Dialog?
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
class hoverDialog(QDialog):
def __init__(self, parent=None):
super().__init__()
self.setAttribute(Qt.WA_DeleteOnClose)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.v = QVBoxLayout()
self.combobox = QComboBox()
self.combobox.addItems(['Work-around','Permanent'])
self.textedit = QPlainTextEdit()
self.v.addWidget(self.combobox)
self.v.addWidget(self.textedit)
self.setLayout(self.v)
#self.setMouseTracking(True)
def leaveEvent(self, event):
self.hide()
return super().leaveEvent(event)
class Table(QWidget):
def __init__(self, parent=None):
super().__init__()
self.label4 = QLabel()
self.label4.setText("hover popup")
self.label4.installEventFilter(self)
self.checkbox1 = QCheckBox()
self.pushButton3 = QPushButton()
self.pushButton3.setText('Generate')
self.pushButton3.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
self.pushButton3.clicked.connect(self.buttonPressed)
self.hbox5 = QHBoxLayout()
self.hbox5.addWidget(self.checkbox1)
self.hbox5.addWidget(self.label4)
self.hbox5.addWidget(self.pushButton3)
self.vbox1 = QVBoxLayout()
self.vbox1.addLayout(self.hbox5)
self.setLayout(self.vbox1)
self.autoResolve = hoverDialog(self)
def eventFilter(self, obj, event):
if obj == self.label4 and event.type() == QtCore.QEvent.Enter and self.autoResolve.isHidden():
self.onHovered()
return super().eventFilter(obj, event)
def onHovered(self):
pos = QtGui.QCursor.pos()
self.autoResolve.move(pos)
self.autoResolve.show()
def buttonPressed(self):
if self.checkbox.isChecked():
print('do something..')
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Table()
form.show()
app.exec_()
Looking at the sources, I believe that the origin of the problem is in Qt's behavior whenever a new popup widget is shown.
I cannot guarantee this at 100%, but in any case the simplest solution is to hide the widget only if the combo popup is not shown:
def leaveEvent(self, event):
if not self.combobox.view().isVisible():
self.hide()
Note that your approach is not really perfect: since the dialog could be shown with a geometry that is outside the current mouse position, it will not be able to hide itself until the mouse actually enters it. You should probably filter FocusOut and WindowDeactivate events also.
I want to build a window which has no title bar, so i do. But it is not any more draggable. You cannot make my window move from here to there.
I know it is because of me, removing the title bar, but how to fix it?
This is my code:
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QWidget
import sys
def window():
app = QApplication(sys.argv)
win = QMainWindow()
win.setGeometry(300, 300, 300, 300)
win.setWindowTitle("Test")
win.setWindowFlags(QtCore.Qt.FramelessWindowHint)
label = QLabel(win)
label.setText("Hello world")
win.show()
sys.exit(app.exec_())
window()
Any help will be appreciated. Please help me with this...
You need to reimplement the mousePress and mouseMove methods of the widget (mouseRelease is technically not mandatory, but is actually required for consistency, as the release event has to be correctly intercepted by Qt to avoid any confusion). The former will get the current cursor position relative to the geometry (self.offset), while the latter will compute the new "window" position by adding the new position to the current one and subtracting the offset.
I would also suggest you to use a QWidget instead of a QMainWindow. While QMainWindow implementation is very similar to that of QWidgets, subclassing a QMainWindow for your purpose might be a bit harder, as it's widget more complex than it seems.
If you only need a QMainWindow to get a status bar, just add a new one to the widget layout; if you also need a menubar, add it to the widget's layout using setMenuBar.
class FramelessWidget(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Test")
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.label = QLabel("Hello world", self)
self.offset = None
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.offset = event.pos()
else:
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.offset is not None and event.buttons() == QtCore.Qt.LeftButton:
self.move(self.pos() + event.pos() - self.offset)
else:
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.offset = None
super().mouseReleaseEvent(event)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = FramelessWidget()
win.setGeometry(300, 300, 300, 300)
win.show()
sys.exit(app.exec_())
I am using qslider in python 3. I can move cursor forward and backward by keyboard event of up, down, left and right arrow. I would like to disable specifically part of them: up and down arrow move cursor while right and left cursor do not. Is it possible to do that?
You have to override the keyPressEvent method:
from PyQt5 import QtCore, QtGui, QtWidgets
class Slider(QtWidgets.QSlider):
def keyPressEvent(self, event):
if event.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right):
return
super(Slider, self).keyPressEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
lay = QtWidgets.QHBoxLayout(w)
slider = Slider()
label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
slider.valueChanged.connect(label.setNum)
label.setNum(slider.value())
lay.addWidget(slider)
lay.addWidget(label)
w.resize(160, 240)
w.show()
sys.exit(app.exec_())
I'm trying to detect mouse clicks for anywhere inside an area with several widgets. For this I'm using the following code:
custom_widget = CustomWidget()
custom_widget.mouse_pressed_signal.connect(self.on_custom_label_mouse_pressed)
main_layout_vbox.addWidget(custom_widget)
hbox = QtWidgets.QHBoxLayout()
custom_widget.setLayout(hbox)
# Adding several widgets to hbox_l6
class CustomWidget(QtWidgets.QWidget):
mouse_pressed_signal = QtCore.pyqtSignal(QtGui.QMouseEvent)
def __init__(self):
super().__init__()
def mousePressEvent(self, i_qmouseevent):
super(CustomWidget, self).mousePressEvent(i_qmouseevent)
logging.debug("======== CustomWidget - mousePressEvent ========")
self.mouse_pressed_signal.emit(i_qmouseevent)
Problem
This works when clicking in any of the child widgets, but there's a problem: If I click between widgets (so in the area of the hbox layout that is not covered by a widget) the mousePressEvent is not captured
Question
How can I solve this problem? (Or is there another approach that you can recommend?) The important thing is that I am able to capture mouse clicks anywhere inside of custom_widget / hbox (see code above)
If you want to listen to other widget's mousePressEvent you can use an eventFilter as I show below:
from PyQt5 import QtCore, QtGui, QtWidgets
import random
class Widget(QtWidgets.QWidget):
mouse_clicked_signal = QtCore.pyqtSignal(QtGui.QMouseEvent, QtWidgets.QWidget)
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
hlay = QtWidgets.QHBoxLayout(self)
for cls in (QtWidgets.QLabel, QtWidgets.QPushButton, QtWidgets.QFrame, QtWidgets.QWidget):
widget = cls()
color = QtGui.QColor(*random.sample(range(255), 3))
widget.setStyleSheet("background-color: {}".format(color.name()))
hlay.addWidget(widget)
for w in self.findChildren(QtWidgets.QWidget) + [self]:
w.installEventFilter(self)
self.resize(640, 480)
def eventFilter(self, watched, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
self.mouse_clicked_signal.emit(event, watched)
return super(Widget, self).eventFilter(watched, event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.mouse_clicked_signal.connect(print)
w.show()
sys.exit(app.exec_())
I've got the following python code which opens a second window. I can't figure out how to add a label or pushbutton to this second window. I thought it would be easy but nothing I try seems to work. Thanks!
from PyQt4 import QtGui, QtCore
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
menu = self.menuBar().addMenu(self.tr('View'))
action = menu.addAction(self.tr('New Window'))
action.triggered.connect(self.handleNewWindow)
def handleNewWindow(self):
window = QtGui.QMainWindow(self)
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
window.setWindowTitle(self.tr('New Window'))
window.show()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(300, 300)
window.show()
sys.exit(app.exec_())
If the two windows are different, it makes more sense to create two class.
I guess the second one doesn't need to be a QMainWindow (= it doesn't need a menu and a toolbar and a status bar etc), so let's just make it a QWidget.
class SecondWindow(QtGui.QWidget):
def __init__(self,parent):
QtGui.QWidget.__init__(self,parent)
self.button=QtGui.QPushButton("my button !")
layout=QtGui.QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.show()
In your main window, you cretae and instance of the class SecondWindow:
class FirstWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
...
self.show()
def handleNewWindow(self):
self.childWindow = SecondWindow(self)
If you just want a TopLevel window, using QtGui.QDialog seems to be more appropriate. To add button and label, you can do something like this:
def handleNewWindow(self):
window = QtGui.QMainWindow(self)
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
window.setWindowTitle(self.tr('New Window'))
button = QtGui.QPushButton("MY BUTTON!") #create button
label = QtGui.QLabel("MY LABEL!") # create label
CentralWidget = QtGui.QWidget() # create an empty widget
CentralWidgetLayout = QtGui.QHBoxLayout() # create a layout
CentralWidgetLayout.addWidget(label) # add your label to the layout
CentralWidgetLayout.addWidget(button) # add your button to the layout
CentralWidget.setLayout(CentralWidgetLayout) # assign your layout to the empty widget
window.setCentralWidget(CentralWidget) #make the assigned widget CentralWidget
window.show()