ProTip: Very Important thing to remember when using QScrollArea - qt

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

Related

I can't see QLabel and QLineEdit widgets in my QMainWindow in Python2

I wrote this code and I don't understand why widgets QLabel and QLineEdit don't show up? Do I have to put them in another class? It's Python2.7 and PySide.
This is how a window looks like when I run the code:
#!/usr/bin/env python
# coding: utf-8
import sys
import crypt
from PySide import QtGui
class MyApp(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.initui()
def initui(self):
# main window size, title and icon
self.setMinimumSize(500, 350)
self.setWindowTitle("Calculate a password hash in Linux")
# lines for entering data
self.saltLabel = QtGui.QLabel("Salt:")
self.saltLine = QtGui.QLineEdit()
self.saltLine.setPlaceholderText("e.g. $6$xxxxxxxx")
# set layout
grid = QtGui.QGridLayout()
grid.addWidget(self.saltLabel, 0, 0)
grid.addWidget(self.saltLine, 1, 0)
self.setLayout(grid)
# show a widget
self.show()
def main():
app = QtGui.QApplication(sys.argv)
instance = MyApp()
instance.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
How about using a QWidget as centralWidget
widget = QWidget()
widget.setLayout(grid)
#add your widgets and...
self.setCentralWidget(widget)
and you don't need to call show() since you do in your __main__
It's up to the owner but i would recommend sublassing a QWidget and leave your QMainWindow instance as concise as possible. An implementation could be:
class MyWidget(QtGui.QWidget):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
grid = QtGui.QGridLayout()
#and so on...
and use this as widget in your QMainWindow instance. This increases readability and maintainability and reusability a lot :)

Why does clicking a QToolButton generate a leaveEvent? Is there a workaround?

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)

QPushButton won't fire clicked slot (method of QObject)

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..."

Attach a QToolButton to bottom right corner of the parent QWidget

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())

Using PyQt and the QTableWidget to set in image in the horizontalHeaderItem

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

Resources