How do I add qtreeview sub items - qt

How can I add children to my current qtreeview items in the code i provided below? I want it to look something like this:
The code below creates a treeview that is sorted but im not entirely clear on how to add children items. Hope you can help, thanks.
import sys
from PySide import QtGui, QtCore
class SortModel(QtGui.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(SortModel, self).__init__(*args, **kwargs)
def lessThan(self, left, right):
leftData = self.sourceModel().data(left)
rightData = self.sourceModel().data(right)
if leftData:
leftData = leftData.lower()
if rightData:
rightData = rightData.lower()
print('L:', leftData, 'R:', rightData)
return leftData < rightData
class Browser(QtGui.QDialog):
def __init__(self, parent=None):
super(Browser, self).__init__(parent)
self.initUI()
def initUI(self):
self.resize(200, 300)
self.setWindowTitle('Assets')
self.setModal(True)
self.results = ""
self.uiItems = QtGui.QTreeView()
self.uiItems.setAlternatingRowColors(True)
self.uiItems.setSortingEnabled(True)
self.uiItems.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.uiItems.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.uiItems.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
self._model = self.create_model(self)
self._spmodel = SortModel(self)
self._spmodel.setSourceModel(self._model)
self._spmodel.setDynamicSortFilter(True)
self.uiItems.setModel(self._spmodel)
grid = QtGui.QGridLayout()
grid.setContentsMargins(0, 0, 0, 0)
grid.addWidget(self.uiItems, 0, 0)
self.setLayout(grid)
self.setLayout(grid)
self.uiItems.doubleClicked.connect(self.doubleClickedItem)
self.show()
def doubleClickedItem(self, item):
name = item.data(role=QtCore.Qt.DisplayRole)
print name
def create_model(self, parent):
items = [
'Cookie dough',
'Hummus',
'Spaghetti',
'Dal makhani',
'Chocolate whipped cream'
]
model = QtGui.QStandardItemModel()
model.setHorizontalHeaderLabels(['Name'])
for item in items:
model.appendRow(QtGui.QStandardItem(item))
return model
def showEvent(self, event):
geom = self.frameGeometry()
geom.moveCenter(QtGui.QCursor.pos())
self.setGeometry(geom)
super(Browser, self).showEvent(event)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
# self.hide()
self.close()
event.accept()
else:
super(Browser, self).keyPressEvent(event)
def main():
app = QtGui.QApplication(sys.argv)
ex = Browser()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

You would do this in your create_model method. There are several different ways of appending child rows and columns. Instead of passing a QStandardItem directly to model.appendRow, create it and save it in a variable. Then you can use QStandardItem.appendRow to add children to your top level rows.
I don't know Python and may get the syntax wrong, but the basic pattern is something like this:
std_item = QtGui.QStandardItem ("Dinner")
child_std_item = QtGui.QStandardItem ("Drinks")
std_item.appendRow (child_std_item)
Alternately, you can do this at the model level using model.insertRow and specify the QModelIndex of the parent item. You can get the QModelIndex of an item using model.indexFromItem. Hopefully that's enough to get you going. Each QStandardItem knows its parent, if any, and its children, so it's usually a matter of having the parent available to add/change/remove children.

Related

python3.4 + pyqt5: Getting x,y out of QMouseEvent

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

PyQt-QtableView Header. How to add top-right small button to hide/show column with checked/unchecked Checkboxes?

With PyQt4, I am using a QtableView with more than 10 columns. The user must have the choice of showing/hiding a column.
This is generally done by adding a small button in the top-right of the table's header. The button shows a menu with checked/unchecked Checkboxes allowing to hide/show columns.
This is an example from Sqlite-Manager Table.
So, I wonder how can I do the same with PyQt's QtableView?
Thanks,
Thank you Kitsune Meyoko, it was a great Idea.. ;)
I found another solution pretty much like yours by using QMenu with Checkable QActions instead of a QPushButton: Let's Go:
import sys
import string
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Header(QHeaderView):
def __init__(self, parent=None):
super(Header, self).__init__(Qt.Horizontal, parent)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.ctxMenu)
self.setup()
#pyqtSlot(bool)
def printID(self, i):
print("id")
if i == False:
self.hideSection(0)
else:
self.showSection(0)
#pyqtSlot(bool)
def printNAME(self, i):
print("name")
if i == False:
self.hideSection(1)
else:
self.showSection(1)
#pyqtSlot(bool)
def printUSERNAME(self, i):
print("username")
if i == False:
self.hideSection(2)
else:
self.showSection(2)
def setup(self):
self.id = QAction("id",self)
self.id.setCheckable(True)
self.id.setChecked(True)
self.connect(self.id, SIGNAL("triggered(bool)"), self, SLOT("printID(bool)"))
self.name = QAction("name",self)
self.name.setCheckable(True)
self.name.setChecked(True)
self.connect(self.name, SIGNAL("triggered(bool)"), self, SLOT("printNAME(bool)"))
self.username = QAction("username",self)
self.username.setCheckable(True)
self.username.setChecked(True)
self.connect(self.username, SIGNAL("triggered(bool)"), self, SLOT("printUSERNAME(bool)"))
def ctxMenu(self, point):
menu = QMenu(self)
self.currentSection = self.logicalIndexAt(point)
menu.addAction(self.id)
menu.addAction(self.name)
menu.addAction(self.username)
menu.exec_(self.mapToGlobal(point))
class Table(QTableWidget):
def __init__(self, parent=None):
super(Table, self).__init__(parent)
self.setHorizontalHeader(Header(self))
self.setColumnCount(3)
self.setHorizontalHeaderLabels(['id', 'name', 'username'])
self.populate()
def populate(self):
self.setRowCount(10)
for i in range(10):
for j,l in enumerate(string.ascii_letters[:3]):
self.setItem(i, j, QTableWidgetItem(l))
if __name__ == '__main__':
app = QApplication(sys.argv)
t = Table()
t.show()
app.exec_()
sys.exit()
In QTableView not have kind of button just like "Sqlite-Manager Table". But your can custom widget by using QtGui.QPushButton and work with QtGui.QMenu together to get column from user. And use QTableView.hideColumn (self, int column) & QTableView.showColumn (self, int column) to hide show your column;
Full example;
import sys
import random
from functools import partial
from PyQt4 import QtGui
class QCustomTableViewWidget (QtGui.QWidget):
def __init__ (self, myQStandardItemModel, *args, **kwargs):
super(QCustomTableViewWidget, self).__init__(*args, **kwargs)
# Layout setup
self.localQTableView = QtGui.QTableView()
self.rightCornerQPushButton = QtGui.QPushButton()
menuQHBoxLayout = QtGui.QHBoxLayout()
menuQHBoxLayout.addStretch(1)
menuQHBoxLayout.addWidget(self.rightCornerQPushButton)
allQVBoxLayout = QtGui.QVBoxLayout()
allQVBoxLayout.addLayout(menuQHBoxLayout)
allQVBoxLayout.addWidget(self.localQTableView)
self.setLayout(allQVBoxLayout)
# Object setup
self.localQTableView.setModel(myQStandardItemModel)
self.rightCornerQPushButton.setText('Show column')
currentQMenu = QtGui.QMenu()
for column in range(myQStandardItemModel.columnCount()):
currentQAction = QtGui.QAction('Column %d' % (column + 1), currentQMenu)
currentQAction.setCheckable(True)
currentQAction.setChecked(True)
currentQAction.toggled.connect(partial(self.setColumnVisible, column))
currentQMenu.addAction(currentQAction)
self.rightCornerQPushButton.setMenu(currentQMenu)
def setColumnVisible (self, column, isChecked):
if isChecked:
self.localQTableView.showColumn(column)
else:
self.localQTableView.hideColumn(column)
def tableView (self):
return self.localQTableView
# Simulate data
myQStandardItemModel = QtGui.QStandardItemModel()
for _ in range(10):
myQStandardItemModel.appendRow([QtGui.QStandardItem('%d' % random.randint(100, 999)), QtGui.QStandardItem('%d' % random.randint(100, 999)), QtGui.QStandardItem('%d' % random.randint(100, 999))])
# Main application
myQApplication = QtGui.QApplication(sys.argv)
myQCustomTableViewWidget = QCustomTableViewWidget(myQStandardItemModel)
myQCustomTableViewWidget.show()
sys.exit(myQApplication.exec_())

PySide crashing when replacing rows in QFormLayout

Using the following code example PySide segfaults when pushing "Add", "Add", "Remove", "Add" and due to some other sequences of interaction.
Python: 2.7.6
PySide: 1.2.1
QtCore: 4.8.5
Code:
from PySide.QtGui import *
from PySide.QtCore import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setObjectName('MainWindow')
self.baseLayout = QWidget(self)
self.v_layout = QVBoxLayout(self.baseLayout)
self.setCentralWidget(self.baseLayout)
self.form_layout = QFormLayout(self.baseLayout)
self.v_layout.addLayout(self.form_layout)
self.button_add = QPushButton(self.baseLayout)
self.button_add.setText("Add")
self.v_layout.addWidget(self.button_add)
self.button_del = QPushButton(self.baseLayout)
self.button_del.setText("Remove")
self.v_layout.addWidget(self.button_del)
self.button_add.clicked.connect(self.add)
self.button_del.clicked.connect(self.remove)
self.fields = []
def add_item(self):
layout = QHBoxLayout(self.parent())
line = QLineEdit(self.parent())
slider = QSlider(self.parent())
layout.addWidget(line)
layout.addWidget(slider)
self.fields.append((layout, line, slider))
self.form_layout.addRow("Test", layout)
def add(self):
for i in range(15):
self.add_item()
def remove(self):
for (layout, line, slider) in self.fields:
line.deleteLater()
slider.deleteLater()
while self.form_layout.itemAt(0):
child = self.form_layout.takeAt(0)
if child.widget():
child.widget().deleteLater()
self.form_layout.update()
self.fields = []
def main():
import sys
app = QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()
if __name__ == '__main__':
main()
Is this the correct way of adding compound widgets (in this case a QLineEdit and a QSlider within a QVBoxLayout) to a form layout? What am I doing wrong?
The correct way of adding "compound widgets" to a QFormLayout is to create a QWidget that will be the parent to that layout.
add_item() should rather look something like this:
def add_item(self):
widget = QWidget(self.parent())
layout = QHBoxLayout(widget)
line = QLineEdit(widget)
slider = QSlider(widget)
layout.addWidget(line)
layout.addWidget(slider)
self.fields.append((layout, widget, line, slider))
self.form_layout.addRow("Test", widget)
(And the widget also has to be removed when deleting the fields).
I think you are not creating the layouts in the right way, for example you are trying to set the layout of base_layout two times. Also you can check for count() on a QLayout to see if it has children:
from PySide.QtGui import *
from PySide.QtCore import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.baseLayout = QWidget(self)
self.v_layout = QVBoxLayout(self.baseLayout)
self.setCentralWidget(self.baseLayout)
self.form_layout = QFormLayout()
self.v_layout.addLayout(self.form_layout)
self.button_add = QPushButton()
self.button_add.setText("Add")
self.v_layout.addWidget(self.button_add)
self.button_del = QPushButton()
self.button_del.setText("Remove")
self.v_layout.addWidget(self.button_del)
self.button_add.clicked.connect(self.add)
self.button_del.clicked.connect(self.remove)
self.fields = []
def add_item(self):
layout = QHBoxLayout()
line = QLineEdit()
slider = QSlider()
layout.addWidget(line)
layout.addWidget(slider)
self.fields.append((layout, line, slider))
self.form_layout.addRow("Test", layout)
def add(self):
for i in range(15):
self.add_item()
def remove(self):
while self.form_layout.count() > 0:
child = self.form_layout.takeAt(0)
widget = child.widget()
if widget:
widget.deleteLater()
self.form_layout.update()
self.fields = []
def main():
import sys
app = QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()
if __name__ == '__main__':
main()

Qt Line drawing to unexpected location

I'm trying to write a drawing program but I'm having problems with drawing lines. When I draw a line on the bottom portion of the QGraphicsView the line is drawn to the center of the widget. Why? I don't think I understand the mapTo functions well enough, but the more I read the Qt docs the more confused I get. Hope someone can help.
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class Main(QWidget):
def __init__(self, parent):
super(Main, self).__init__(parent)
self.resize(300, 300)
vBox = QVBoxLayout(self)
view = View(self)
vBox.addWidget(view)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
sys.exit()
class View(QGraphicsView):
def __init__(self, parent):
super(View, self).__init__(parent)
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
def mousePressEvent(self, event):
self.start = event.pos()
def mouseReleaseEvent(self, event):
self.stop = event.pos()
self.line = Line(self, self.start, self.stop)
self.scene.addItem(self.line)
class Line(QGraphicsLineItem):
def __init__(self, parent, *args):
# args = start, stop
points = map(parent.mapToScene, args)
(start, stop) = map(QPointF, points)
self.line = QLineF(start, stop)
super(Line, self).__init__(self.line)
def run():
app = QApplication(sys.argv)
a = Main(None)
a.show()
sys.exit(app.exec_())
run()
If you don't set a rect for your scene, one will be calculated automatically and the view will centre itself on the objects within it.
To fix this, add the following to the end of View.__init__():
self.setSceneRect(QRectF(self.viewport().rect()))

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