I have the code below for a interactive label in PyQt4 that can be clicked, right clicked and scrolled. I am converting the code for PyQt5 and lots of things in my code are right now based on this element.
class ExtendedQLabel(QLabel):
def __init(self, parent):
super().__init__(parent)
def mousePressEvent(self, ev):
if ev.button() == QtCore.Qt.RightButton:
self.emit(SIGNAL('rightClicked()'))
else:
self.emit(SIGNAL('clicked()'))
def wheelEvent(self, ev):
self.emit(SIGNAL('scroll(int)'), ev.delta())
How do I make this PyQt5 compatible?
Ok, after a lot of things I understood what I was doing wrong:
from PyQt5 import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class ExtendedQLabel(QLabel):
def __init(self, parent):
super().__init__(parent)
clicked = pyqtSignal()
rightClicked = pyqtSignal()
def mousePressEvent(self, ev):
if ev.button() == Qt.RightButton:
self.rightClicked.emit()
else:
self.clicked.emit()
if __name__ == '__main__':
app = QApplication([])
eql = ExtendedQLabel()
eql.clicked.connect(lambda: print('clicked'))
eql.rightClicked.connect(lambda: print('rightClicked'))
eql.show()
app.exec_()
In the line with the text clicked = pyqtSignal() and rightClicked = pyqtSignal()what ties those signals to that class that makes the code above work? Well the answer is the correct indentation, correct indentation will nest the variable to the class instead of randomly creating a variable that has no use. It took me a lot of time to perceive this, so thought lefting this here could be useful if someone google this.
from PyQt5 import QtGui,QtCore,QtWidgets
class Clickable_Label(QtWidgets.QLabel):
def __init__(self):
super().__init__()
clicked = QtCore.pyqtSignal() # signal when the text entry is left clicked
def mousePressEvent(self, event):
self.clicked.emit()
QtWidgets.QLabel.mousePressEvent(self, event)
myLabel=Clickable_Label()
myLabel.setText("Clickable_Label")
myLabel.board_rule_download.clicked.connect(lambda: print('clicked'))`enter code here`
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'm trying to figure out how dnd works with (py)qt. I tried several example applications and.. hm was wondering: how do I see what data is actually inside a drag/drop event? I tried printing the event, the dict of the event, the mimeData().text().. but no mater what i drag in there, there seems to be no data whatsoever. Eg if I drag over a '.desktop' file from /usr/share/applications, shouldn't there be info inside the drag event?
see this eg. application:
#!/usr/bin/python
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtCore import Qt
class DragFromWidget(QtGui.QDockWidget):
def __init__(self, parent=None):
super(DragFromWidget, self).__init__(parent=parent)
self.layout().addWidget(QtGui.QLabel("Label!"))
class DragToWidget(QtGui.QDockWidget):
def __init__(self, parent=None):
super(DragToWidget, self).__init__(parent=parent)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
#HOW?
print event
print event.__dict__
print event.mimeData()
print event.mimeData().__dict__
print event.mimeData().text()
event.accept()
def dropEvent(self, event):
#HOW?
print event
print event.__dict__
print event.mimeData()
print event.mimeData().__dict__
print event.mimeData().text()
class SandboxApp(QtGui.QApplication):
def __init__(self, *args, **kwargs):
super(SandboxApp, self).__init__(*args)
self.mainwindow = MainWindow()
self.mainwindow.show()
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks|QtGui.QMainWindow.AnimatedDocks)
self.addDockWidget(Qt.LeftDockWidgetArea, DragFromWidget())
self.addDockWidget(Qt.RightDockWidgetArea, DragToWidget())
def main():
app = SandboxApp(sys.argv)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Example code I am using:
import sys
from PyQt5 import QtCore
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.open_about = False
self.openAction = QtWidgets.QAction('About', self)
self.openAction.triggered.connect(self.aboutDialog)
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
fileMenu.addAction(self.openAction)
self.calendar = QtWidgets.QCalendarWidget(self)
self.setCentralWidget(self.calendar)
def about_state_upd(self, value):
self.open_about = value
def aboutDialog(self):
self._about = AboutDialog(self)
self._about.exec_()
def hideEvent(self, hideEvent):
if self.open_about == True:
self._about.setVisible(False)
def showEvent(self, showEvent):
if self.open_about == True:
if self._about.isHidden() == True:
self._about.setModal(True)
self._about.setVisible(True)
class AboutDialog(QtWidgets.QDialog):
def __init__(self, parent):
super(AboutDialog, self).__init__(parent)
self.setMinimumSize(400, 350)
self.parent().about_state_upd(True)
def closeEvent(self, closeEvent):
self.parent().about_state_upd(False)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app_window = MainWindow()
app_window.showMaximized()
sys.exit(app.exec_())
This code basically works, but seems very complicated. Is there a simpler / cleaner way to make it so that when the modal QDialog is open, if the QMainWindow is minimized, the QDialog also gets minimized too (and reverse when QMainWindow is restored)?
Code is running on KDE Neon (Kubuntu-based distro).
May be you can use this: http://korbinin.blogspot.fr/search/label/minimize%20button
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MainForm(QDialog):
def __init__(self, fn=None,parent=None):
super(MainForm, self).__init__(parent,\
flags=Qt.WindowMinimizeButtonHint|Qt.WindowMaximizeButtonHint)
Thanks to the people on the PyQt Mailing list, I managed to get a workaround for KDE. Instead of using exec_(), I am just using show() - then I use setDisabled() on QMainWindow to make dialog act in a modal fashion. Here is a (very quick and basic) example for anyone interested:
import sys
from PyQt5 import QtCore
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.openAction = QtWidgets.QAction('About', self)
self.openAction.triggered.connect(self.aboutDialog)
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
fileMenu.addAction(self.openAction)
self.calendar = QtWidgets.QCalendarWidget(self)
self.setCentralWidget(self.calendar)
def aboutDialog(self):
self._about = AboutDialog(self)
self.setDisabled (True)
self._about.show()
def enableWidgets(self):
self.setDisabled(False)
class AboutDialog(QtWidgets.QDialog):
def __init__(self, parent):
super(AboutDialog, self).__init__(parent)
self.setMinimumSize(400, 350)
def closeEvent(self, parent):
self.parent().enableWidgets()
def changeEvent(self, event):
if event.type() == QtCore.QEvent.WindowStateChange:
if self.windowState() & QtCore.Qt.WindowMinimized:
self.parent().showMinimized()
else:
self.parent().showMaximized()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app_window = MainWindow()
app_window.showMaximized()
sys.exit(app.exec_())
Link to PyQt Mailing List posts.
This question already has answers here:
How to customize title bar and window of desktop application
(2 answers)
Closed 6 years ago.
Is there a way I can use Qt Designer to design the Frame and the Title Bar ?
If so how can I make it work with existing code ?
Here is the existing code:
import sys
from PyQt5 import QtCore, uic
from PyQt5.QtWidgets import QApplication, QDialog
class Actions(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.ui = uic.loadUi("console_gui.ui")
self.ui.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.ui.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Actions()
sys.exit(app.exec_())
If not, are there links to open source PyQt5 projects that successfully use FramelessWindowHint ?
PyQt5 has no documentation whatsoever on any of this. They just link to the C++ versions.
You could use QPushButtons and QHBoxLayout.
class Actions(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.resize(500, 600)
#
self.otherFrame = QFrame(self)
# There are two Buttons and using them custom title bar.
# We could give them string or pictures.
self.oneButton = QPushButton('one')
self.twoButton = QPushButton('two')
# We must be give them some functions.
self.oneButton.clicked.connect(self.oneClicked)
self.twoButton.clicked.connect(self.twoClicked)
# And some layouts.
# This is a layout of horizontal.
#
self.layouts = QHBoxLayout()
# This is a layout of vertical.
self.layouts2 = QVBoxLayout()
self.layouts.addWidget(self.oneButton)
self.layouts.addWidget(self.twoButton)
self.layouts.addStretch(1)
self.layouts2.addLayout(self.layouts)
self.layouts2.addWidget(self.otherFrame)
self.setLayout(self.layouts2)
self.show()
def oneClicked(self):
print(1)
def twoClicked(self):
print(2)
def mousePressEvent(self, event):
if event.buttons() == Qt.LeftButton:
self.m_drag = True
self.m_DragPosition = event.globalPos()-self.pos()
event.accept()
def mouseMoveEvent(self, event):
try:
if event.buttons() and Qt.LeftButton:
self.move(event.globalPos()-self.m_DragPosition)
event.accept()
except AttributeError:
pass
def mouseReleaseEvent(self, event):
self.m_drag = False
Like this.
I need to enable a button in my app whenever something is dropped to my custom QTreeWidget.
I sub-classed QTreeWidget to implement drag and drop of custom data. But I'm not able to find a way to get notified when something is dropped into my custom QTreeWidget. I couldn't find a QTreeWidget signal to do this. Of course, the QTreeWidget's dropEvent() will be called each time something is dropped but that doesn't help much to achieve what I'm trying to do.
Here's where I instantiate the custom QTreeWidget that accepts drops from another widget,
from PyQt4 import QtCore, QtGui
import MyTreeWidget
class TestWindow(QtGui.QDialog):
def __init__(self, parent=None):
super(TestWindow, self).__init__(parent)
self.myTreeWidget = MyTreeWidget.MyTreeWidget()
...
#self.myTreeWidget.onItemDropped.connect(self.doSomethingOnItemDropped) <== I am looking for something like this
def doSomethingOnItemDropped(self):
# Enable certain button
...
And then, here is how I sub-classed QTreeWidget,
import sys
from PyQt4 import QtGui, QtCore
class MyTreeWidget(QtGui.QTreeWidget):
def __init__(self, parent=None):
super(MyTreeWidget, self).__init__(parent)
self.setAcceptDrops(True)
def dropEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
event.acceptProposedAction()
data = event.mimeData().data("application/x-icon-and-text")
stream= QtCore.QDataStream(data, QtCore.QIODevice.ReadOnly)
text = QtCore.QString()
icon = QtGui.QIcon()
stream >> text >> icon
item = QtGui.QTreeWidgetItem(self)
item.setText(0, text)
item.setIcon(0, icon)
self.addTopLevelItem(item)
else:
event.ignore()
def dragEnterEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat("application/x-icon-and-text"):
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
event.ignore()
Any ideas? Thanks!
UPDATE : This is what worked for me
Based on comment by #ekhumoro I defined a custom signal itemDropped for my custom QTreeWidget which emits in dropEvent() event handler.
import sys
from PyQt4 import QtGui, QtCore
class MyTreeWidget(QtGui.QTreeWidget):
itemDropped = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(MyTreeWidget, self).__init__(parent)
self.setAcceptDrops(True)
def dropEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
event.acceptProposedAction()
data = event.mimeData().data("application/x-icon-and-text")
stream= QtCore.QDataStream(data, QtCore.QIODevice.ReadOnly)
text = QtCore.QString()
icon = QtGui.QIcon()
stream >> text >> icon
item = QtGui.QTreeWidgetItem(self)
item.setText(0, text)
item.setIcon(0, icon)
self.addTopLevelItem(item)
self.itemDropped.emit()
else:
event.ignore()
And in my app,
from PyQt4 import QtCore, QtGui
import MyTreeWidget
class TestWindow(QtGui.QDialog):
def __init__(self, parent=None):
super(TestWindow, self).__init__(parent)
self.myTreeWidget = MyTreeWidget.MyTreeWidget()
self.myTreeWidget.itemDropped.connect(self.doSomethingOnItemDropped)
...
def doSomethingOnItemDropped(self):
# Enable certain button
...
You could define a custom signal and emit it from dropEvent:
class MyTreeWidget(QtGui.QTreeWidget):
itemDropped = QtCore.pyqtSignal()
def dropEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
...
self.itemDropped.emit()
In PySide2 you can use this:
class MyTreeWidget(QtGui.QTreeWidget):
itemDropped = QtCore.Signal()
def dropEvent(self, event):
if (event.mimeData().hasFormat('application/x-icon-and-text')):
self.itemDropped.emit()