I have a QObject, which works as a controller.
This QObject has a reference to a QPushButton.
This QOjbect has a method set to be fired upon QPushButton's clicked event.
Ex:
class MyController(QObject):
def __init__(self, parent=None):
super(MyController, self).__init__(parent)
self.some_ref = ....
self.button = self.some_ref.button (returns QPushButton)
self.button.clicked.connect(self.button_clicked)
# #Slot(type)
def button_clicked(self):
print 'button clicked: ', self.sender()
# print 'button clicked (no sender req.)
Here, the button_clicked won't get fired.
I tried decorating it with #Slot(), #Slot(QObject), #Slot(type), #Slot(str), #Slot(int) but still won't work.
What am I doing wrong?
If I use ..clicked.connect(lambda: self.button_clicked) it of course works. So I assume this is a type mismatch but shouldn't #Slot(..) decoration have fixed it?
Thank you.
I don't know if the problem is that #Slot() is commented (have a # at the beginning), but this code works for me (it's in python 3, but just change the print line)
import sys
from PySide.QtGui import *
from PySide.QtCore import *
class Window(QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.button = QPushButton()
self.button.setText("Test")
self.setCentralWidget(self.button)
def GetButton(self):
return self.button
class MyController(QObject):
def __init__(self, parent=None):
super(MyController, self).__init__(parent)
self.button = parent.GetButton() #(returns QPushButton)
self.button.clicked.connect(self.button_clicked)
#Slot()
def button_clicked(self):
print('button clicked: ', self.sender())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
controller = MyController(window)
window.show()
app.exec_()
sys.exit(0)
May be could try the released signal instead of the clicked signal, because the clicked signal is emitted when the button is activated (i.e. pressed down then released while the mouse cursor is inside the button).
Or you could try the #method 2 of connecting the signals.
class MyController(QObject):
def __init__(self, parent=None):
super(MyController, self).__init__(parent)
self.some_ref = ....
self.button = self.some_ref.button
# method 1
self.button.released.connect(self.button_clicked)
# method 2
self.connect(self.button, SIGNAL('released()'), self.button_clicked)
def button_clicked(self):
print "yipee it works..."
Related
Today I spent 3 hours in figuring why my QScrollArea was not working. And finally found that the reason was the components which I was dynamically adding to the ScrollArea didn't have any Layout so it was not able to compute the minimum size of it while adding.
So If anyone is adding elements dynamically to the QScrollArea, make sure your widgets have some area. I saw lot of posts where people where facing the same issue and there was no direct answer that layout is the cause so this is like a post notifying to the all users to make sure you first have a layout in all of your widgets you want to append to QScrollArea.
Thank you
An example would be
import sys
from PyQt4 import QtCore, QtGui, uic
mainBase, mainForm = uic.loadUiType("main.ui")
labelBase, labelForm = uic.loadUiType("labelsForScroll.ui")
dataBase, dataForm = uic.loadUiType("data.ui")
emptyBase, emptyForm = uic.loadUiType("empty.ui")
class Data(dataBase, dataForm):
def __init__(self, parent=None):
super(dataBase, self).__init__(parent)
self.setupUi(self)
def setSl(self, num):
self.sl.setText(str(num))
class Label(labelBase, labelForm):
def __init__(self, parent=None):
super(labelBase, self).__init__(parent)
self.setupUi(self)
class Empty(emptyBase, emptyForm):
def __init__(self, parent=None):
super(emptyBase, self).__init__(parent)
self.setupUi(self)
class Main(mainBase, mainForm):
def __init__(self, parent=None):
super(mainBase, self).__init__(parent)
self.setupUi(self)
self.__l = Label()
#self.vert.addWidget(l)
self.__maxSlNum = 0
self.__datLis =[]
#self.addField()
self.__e = Empty()
self.__e.layout().addWidget(self.__l)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.__e)
QtCore.QObject.connect(self.add, QtCore.SIGNAL("clicked()"), self.addField)
def addField(self):
d = Data()
self.__maxSlNum+=1
d.setSl(self.__maxSlNum)
self.__datLis.append(d)
lay = self.__e.layout()
lay.addWidget( d)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
app.setStyle("cleanlooks")
m = Main()
m.show()
sys.exit(app.exec_())
links for the ui file can be found here links
python3.4+pyqt5
I'm trying to write a program in which buttons shall create other buttons by dragging and press_releasing to form a representation of a rolling mill street.
I created a class Button which does the moving and handles several clicking events for the buttons in creation. This part is working fine.
Where I'm stuck is, I want to read the mouse position where these button should be created.
Here's the code:
def createConnects(self):
self.id000001.released.connect(self.do_something)
self.id000002.released.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
self.bname = 'Button'
self.button = Button(self.bname, self)
posx = QtGui.QMouseEvent.x()
print(posx)
posy = QtGui.QMouseEvent.y
print(posy)
sender = self.sender()
print(str(sender.objectName()))
#self.button.move(posx, posy)
qmouseevent provides several functions, but the posx line gives me this error:
TypeError: QMouseEvent.x(): first argument of unbound method must have type 'QMouseEvent'
posy-line gives < built-in function y > which isn't what I want, too, but clear.
MouseTracking is switched on in MainWindow class.
Maybe, normaly one would do that by using event in the def line but since it's a slot, that would lead to other problems.
Any hints to get along?
Update:
As recommended here's the full code of the prototype:
#!/usr/bin/env python3.4
import sys, os, math, shutil, re
from subprocess import call, Popen
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.uic import loadUiType
Ui_MainWindow, QMainWindow = loadUiType('mainwindow.ui')
def main(argv):
app = QtWidgets.QApplication(argv)
mainwindow = MyMainWindow()
mainwindow.show()
sys.exit(app.exec_())
class Button(QtWidgets.QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
self.button = QtWidgets.QPushButton()
def mouseMoveEvent(self, event):
if event.buttons() != QtCore.Qt.RightButton:
return
mimeData = QtCore.QMimeData()
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(event.pos() - self.rect().topLeft())
dropAction = drag.exec_(QtCore.Qt.MoveAction)
def mousePressEvent(self, event):
QtWidgets.QPushButton.mousePressEvent(self, event)
if event.button() == QtCore.Qt.LeftButton:
print(event.button(),' pressed')
class MyMainWindow(QtWidgets.QMainWindow,Ui_MainWindow):
def __init__(self, *args):
QtWidgets.QMainWindow.__init__(self, *args)
self.setupUi(self)
self.setMouseTracking(True)
self.createConnects()
def dragEnterEvent(self, event):
event.accept()
def dropEvent(self, event):
position = event.pos()
self.button.move(position)
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
def createConnects(self):
self.id000001.released.connect(self.do_something)
self.id000002.released.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
print('do_something')
self.bname = 'Button'
self.button = QtWidgets.QPushButton()
#self.button = Button(self.bname, self)
self.button.move(100, 65)
#posx = QtGui.QMouseEvent.x()
#print(posx)
#posy = QtGui.QMouseEvent.y
#print(posy)
sender = self.sender()
print(str(sender.objectName()))
#self.button.move(posx, posy)
if __name__ == '__main__':
main(sys.argv)
cheers,
Christian
For some reason, whenever the menu-button part of the QToolButton gets clicked, it generates a momentary leaveEvent (at least when it is in a toolbar). I even tested underMouse() in the leaveEvent, and it returns false. Why is this? Is there a way to fix this?
Sample for testing (Py3.3, change super() for py2.7):
from PyQt4.QtGui import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
toolbar = QToolBar(self)
toolbar.addWidget(ToolButton())
class ToolButton(QToolButton):
def __init__(self, parent=None):
super().__init__(parent)
self.setText('test')
self.setPopupMode(QToolButton.MenuButtonPopup)
self.setMenu(QMenu())
self.menu().addAction('Stub')
def enterEvent(self, event):
print('entered')
super().enterEvent(event)
def leaveEvent(self, event):
print('left')
super().leaveEvent(event)
if __name__ == '__main__':
import sys
application = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(application.exec_())
The following can be used to double check; unlike leaveEvent, it always returns the correct information:
def leaveEvent(self, event):
if not QApplication.widgetAt(QCursor().pos()) is self:
#do stuff
super().leaveEvent(event)
I am faced with this problem and being a Qt noob am not able to fix it.
Basically, I instantiated a QToolButton and parented it to QTreeWidget. The QTreeWidget is inside a vertical layout and when I try to change the position of the tool button inside the QTreeWidget using QTreeWidget.size() it gives me very unexpected and wrong results.
Can anyone help me with this? Will deeply appreciate the help. Thanks!
You haven't posted any examples of what you are actually doing, but here is how to attach a button to the lower right of the tree widget:
Edit: I have replaced my answer after seeing that you want to composite the widget OVER the tree
Using an eventFilter
from PyQt4 import QtCore, QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.resize(640,480)
self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0)
self.tree = QtGui.QTreeWidget(self)
self.tree.installEventFilter(self)
self.layout.addWidget(self.tree)
self.button = QtGui.QToolButton(self.tree)
self.button.setText("FOO")
self.button.setMinimumSize(100, 30)
def eventFilter(self, obj, event):
if obj is self.tree and event.type() == event.Resize:
self.alignTreeButton()
return False
def alignTreeButton(self):
padding = QtCore.QSize(5,5) # optional
newSize = self.tree.size() - self.button.size() - padding
self.button.move(newSize.width(), newSize.height())
if __name__ == "__main__":
app = QtGui.QApplication([])
w = Widget()
w.show()
w.raise_()
app.exec_()
The button is just parented to the tree, and we install the event filter on the tree to catch resize events. Once the tree is resized, we take its size, subtract the size of the button, and then move the button.
Using composition
I believe its more efficient to actually subclass the QTreeWidget, compose it with the QToolButton as a member, and then overload the resizeEvent() locally to handle the resize. First off this makes the behavior handling local to the TreeWidget, which is cleaner. Also, I believe it reduces the overhead that an EventFilter would add to your main window. The eventFiler would be a python callable that is called many more times because of it handling every event for the object. Whereas the local resizeEvent() for the TreeWidget is only called during the resize.
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.resize(640,480)
self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0)
self.tree = TreeWidget(self)
self.layout.addWidget(self.tree)
class TreeWidget(QtGui.QTreeWidget):
def __init__(self, *args, **kwargs):
super(TreeWidget, self).__init__(*args, **kwargs)
self.button = QtGui.QToolButton(self)
self.button.setText("FOO")
self.button.setMinimumSize(100, 30)
def resizeEvent(self, event):
super(TreeWidget, self).resizeEvent(event)
self.alignTreeButton()
def alignTreeButton(self):
padding = QtCore.QSize(5,5) # optional
newSize = self.size() - self.button.size() - padding
self.button.move(newSize.width(), newSize.height())
I tried sub-classing the QTableWidgetItem and then set my horizontalHeaderItem with that new sub-classed class. for instance:
class ImageWidget(QtGui.QTableWidgetItem):
def __init__(self, imagePath, parent):
super(ImageWidget, self).__init__(parent)
self.picture = QtGui.QPixmap(imagePath)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(0, 0, self.picture)
class showTable(QtGui.QDialog):
def __init__(self, parent=None):
tableWidget = QtGui.QTableWidget(10, 2)
imagePath = "C:/Documents and Settings/pwr37669/workspace/Pro_GUI_Py/images/led_green.gif"
item = ImageWidget(imagePath, QtGui.QTableWidgetItem())
tableWidget.setHorizontalHeaderItem(0, item)
tableWidget.show()
I know that code won't work, but, I'm trying to get something like that to work.
I also need to change the results of clicking on a cell or the header.
Any help would be greatly appreciated.
Thanks,
Stephen
The following seems to do the trick:
class ImgWidget1(QtGui.QLabel):
def __init__(self,imagePath, parent=None):
super(ImgWidget1, self).__init__(parent)
pic = QtGui.QPixmap(imagePath)
self.setAlignment(QtCore.Qt.AlignCenter)
self.setPixmap(pic)
And then when I wanted to add an image I did this:
self.tableWidget.setCellWidget(rows, cells, ui_polling.ImgWidget1(imagePath))
Hope this helps someone out there.
Pls, check if an example below would work for you:
import sys
from PyQt4 import QtGui, QtCore
class MainForm(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
tableWidget = QtGui.QTableWidget()
tableWidget.setRowCount(3)
tableWidget.setColumnCount(2)
for column in range(0, 2):
for row in range(0, 3):
print row, column
item = QtGui.QTableWidgetItem("new item")
tableWidget.setItem(row, column, item)
headerItem = QtGui.QTableWidgetItem("Header Test")
headerItem.setIcon(QtGui.QIcon(QtGui.QPixmap("your_image.png")))
headerItem.setTextAlignment(QtCore.Qt.AlignVCenter);
tableWidget.setHorizontalHeaderItem(0, headerItem)
tableWidget.itemClicked.connect(self.on_tableWidget_itemClicked)
tableWidget.connect(tableWidget.horizontalHeader(), QtCore.SIGNAL('sectionClicked(int)'), self.on_headersection_clicked)
self.setCentralWidget(tableWidget)
def on_tableWidget_itemClicked(self, item):
print "item clicked " + item.text()
def on_headersection_clicked(self, item):
print "header section clicked " + str(item)
def main():
app = QtGui.QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
main()
hope this helps, regards