Crashing when using QAbstractTableModel with QTreeView - python

Since it appears there is no "dedicated" Abstract Model "designed" purposely for QTreeView (for QListView there is QAbstractListModel and for QTableView there is QAbstractTableModel) and since I need to be able to display the headers I opted to use Table's Abstract model: QAbstractTableModel with 'QTreeView'. The code runs fine but if a plus signed is clicked it crashes immidiately. Shouldn't be QAbstractTableModel used with 'QTreeView'? What Abstract model to use?
import os,sys
from PyQt4 import QtCore, QtGui
app=QtGui.QApplication(sys.argv)
elements={'Animals':{1:'Bison',2:'Panther',3:'Elephant'},'Birds':{1:'Duck',2:'Hawk',3:'Pigeon'},'Fish':{1:'Shark',2:'Salmon',3:'Piranha'}}
class Model(QtCore.QAbstractTableModel):
def __init__(self):
QtCore.QAbstractListModel.__init__(self)
self.items=[]
self.modelDict={}
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.items)
def columnCount(self, index=QtCore.QModelIndex()):
return 3
def data(self, index, role):
if not index.isValid() or not (0<=index.row()<len(self.items)): return QtCore.QVariant()
if role==QtCore.Qt.DisplayRole: return QtCore.QVariant(self.items[index.row()])
def buildItems(self):
totalItems=self.rowCount()
for key in self.modelDict:
self.beginInsertRows(QtCore.QModelIndex(), totalItems+1, 0)
self.items.append(key)
self.endInsertRows()
class TreeView(QtGui.QTreeView):
def __init__(self):
super(TreeView, self).__init__()
self.model= Model()
self.model.modelDict=elements
self.model.buildItems()
self.setModel(self.model)
self.show()
window=TreeView()
sys.exit(app.exec_())

You cannot use QAbstractTableModel properly with a QTreeView, as that class is only for QTableView. You must instead inherit QAbstractItemModel, (which is what both QAbstractTableModel and QAbstractListModel inherit), and implement index(), parent(), rowCount(), columnCount(), and data(), as described in the subclassing section of Qt's fine manual. For a QTreeView, parent() in specific is very important to pay attention to, as it tells the QTreeView if an item is at the top level or if it's a child of another item in the tree.
I believe the primary motivation for not having a QAbstractTreeModel class in Qt is because you would need to override all of these methods to create a properly expressive tree model, already.

Related

PushButton in Qt Model/View Table

I'm writing a custom TableModel in PyQt5, inheriting from QtCore.QAbstractTableModel. I want my Table to have one column with CheckBoxes only, no text, and one column with a PushButton in each row.
I tried to return a QPushButton object in the data method for the Qt.Display role, but apparently this is not possible.
So my question is: Can I implement in the Model itself that it returns certain widgets for certain cells? In my opinion, that is the job of the model, but how do I achieve this?
My second question would be how I have to assign the slots so that I know from which of the buttons (from which row) the action occurred?
Explanation:
Can I implement in the Model itself that it returns certain widgets for certain cells? In my opinion, that is the job of the model, but how do I achieve this?
It could be that the model provides the widgets but it is not common. In general, the model provides the information that will be displayed through a delegate, and is the delegate that can provide widgets. There is also the alternative of setting widgets on the unlinked view of the model using the setIndexWidget method.
Considering the first case, the solution is to implement a delegate.
how I have to assign the slots so that I know from which of the buttons (from which row) the action occurred?
In general the widgets that are used as editors so you must update the model, and then the model can notify other elements through the dataChanged signal as for example if the user wrote a text or changed the status of a checkbox, but in the case of the clicked not notify a permanent change but temporary. Considering this, the delegate could present a signal.
Solution:
from PyQt5 import QtCore, QtGui, QtWidgets
class PushButtonDelegate(QtWidgets.QStyledItemDelegate):
clicked = QtCore.pyqtSignal(QtCore.QModelIndex)
def paint(self, painter, option, index):
if (
isinstance(self.parent(), QtWidgets.QAbstractItemView)
and self.parent().model() is index.model()
):
self.parent().openPersistentEditor(index)
def createEditor(self, parent, option, index):
button = QtWidgets.QPushButton(parent)
button.clicked.connect(lambda *args, ix=index: self.clicked.emit(ix))
return button
def setEditorData(self, editor, index):
editor.setText(index.data(QtCore.Qt.DisplayRole))
def setModelData(self, editor, model, index):
pass
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QTableView()
model = QtGui.QStandardItemModel(0, 2)
for i in range(10):
it1 = QtGui.QStandardItem()
it1.setData(QtCore.Qt.Checked, QtCore.Qt.CheckStateRole)
it1.setFlags(it1.flags() | QtCore.Qt.ItemIsUserCheckable)
it2 = QtGui.QStandardItem()
it2.setData("text-{}".format(i), QtCore.Qt.DisplayRole)
model.appendRow([it1, it2])
# pass the view as parent
delegate = PushButtonDelegate(w)
w.setItemDelegateForColumn(1, delegate)
def on_clicked(index):
print(index.data())
delegate.clicked.connect(on_clicked)
w.setModel(model)
w.show()
sys.exit(app.exec_())

what is the best way to embed program logic that seats in some algorithm class into pyqt4 user interface

class SomNetwork(object):
def __init__(self, dataset):
# some parameters that are not important here
pass
def one_step_learn(self, k, sigma_0, gamma_0, alfa, mcolor,population_of_ids):
pass
def learn(self):
pass
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_QSom()
self.ui.setupUi(self)
self.frame = MyFrame(self)
self.setCentralWidget(self.frame)
self.frame.start()
self.show()
class MyFrame(QtGui.QFrame):
simulationSpeed = 5000
def __init__(self, parent):
super(Ramka, self).__init__(parent)
self.init_Board()
def init_Board(self):
self.timer = QtCore.QBasicTimer()
I would like to be able to interact with SomNetwork class in order to be able to call its methods from within inside of the MyFrame class methods. Are there any special design patterns that would suit this example the most or should I just add instance of the class as a field to the MyFrame class.
Thanks of your help in advance!
I don't know if your question is heading towards this, but for your network you could try to subclass a QtCore.QObject instead. On the whole you should instance your network as a member of your QMainWindow, so your frame and the net can interact via signals and slots.
... By the way, there is a little flaw in your code, when you call the constructor of a QWidget in the subclass of a QMainWindow.

Drop to QTDesigner using PyQt4

Using PyQt4 + QT4.8.4, I'd like to drop (external) textual content to a widget defined as a PyQt4 plugin in QtDesigner
I use two python classes :
widgetlabelplugin.py inherited from QPyDesignerCustomWidgetPlugin
widgetlabel.py inherited from QLabel
Overriding the dropEvent in (widgetlabel.py), I'm able to retrieve "external textual content" and to set _model property.
I do the following steps :
Launch designer by previously setting PYQTDESIGNERPATH to the
.py path
Create a dialog without button
Drop a PyGMT/ WiddgetLabel on the dialog
Drop a "textual content" (from notepad) to the widgetlabel label
-> at this step, the label is updated on the dialog but not on the properties browser on the right
Save Dialog from Qt designer tool bar
-> ui file doesn't contain any "textual content" neither for QLabel/text, nor WidgetLabel/model
In Qt designer, If I select dialog background and reselect WidgetLabel, properties are updated in the browser, they are still not saved if I save the ui !
python class : widgetlabelplugin.py
# A demonstration custom widget plugin for PROJECT Qt Designer.
from PyQt4 import QtGui, QtDesigner
from widgetlabel import WidgetLabel
# This class implements the interface expected by Qt Designer to access the
# custom widget. See the description of the QDesignerCustomWidgetInterface
# class for full details.
class WidgetLabelPlugin(QtDesigner.QPyDesignerCustomWidgetPlugin):
# Initialise the instance.
def __init__(self, parent=None):
super(WidgetLabelPlugin, self).__init__(parent)
self._initialized = False
# Initialise the custom widget for use with the specified formEditor
# interface.
def initialize(self, formEditor):
if self._initialized:
return
self._initialized = True
# Return True if the custom widget has been intialised.
def isInitialized(self):
return self._initialized
# Return a new instance of the custom widget with the given parent.
def createWidget(self, parent):
return WidgetLabel(parent)
# Return the name of the class that implements the custom widget.
def name(self):
return "WidgetLabel"
# Return the name of the group to which the custom widget belongs. A new
# group will be created if it doesn't already exist.
def group(self):
return "PyGMT"
# Return the icon used to represent the custom widget in Designer's widget
# box.
def icon(self):
return QtGui.QIcon(":/designer/frame.png")
# Return a short description of the custom widget used by Designer in a
# tool tip.
def toolTip(self):
return "Satis demonstration widget"
# Return a full description of the custom widget used by Designer in
# "What's This?" help for the widget.
def whatsThis(self):
return "WidgetLabel is a demonstration custom widget written in Python " \
"using PyQt."
# Return True if the custom widget acts as a container for other widgets.
def isContainer(self):
return False
# Return the name of the module containing the class that implements the
# custom widget. It may include a module path.
def includeFile(self):
return "WidgetLabel"
python class : widgetlabel.py
#############################################################################
##
## This file is part of the examples of PROJECT Qt Designer.
##
#############################################################################
from PyQt4 import QtCore, QtGui
# This is the class that implements the custom widget for PROJECT.
class WidgetLabel(QtGui.QLabel):
#model changed signal
modelChanged = QtCore.pyqtSignal(QtCore.QString)
# Initialise the instance.
def __init__(self, parent=None):
super(WidgetLabel, self).__init__(parent)
self.setAcceptDrops(True)
self.setText("Label")
# Initialise the model property.
self._model = None
###########################################################################
# DRAG & DROP PART #
###########################################################################
def dragEnterEvent(self, e):
if e.mimeData().hasFormat("text/plain"):
e.setDropAction(QtCore.Qt.CopyAction)
e.accept()
else:
e.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat("text/plain"):
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
event.ignore
def dropEvent(self, e):
e.acceptProposedAction()
bStream = e.mimeData().retrieveData("text/plain",
QtCore.QVariant.ByteArray)
self.setModel(QtCore.QString(str(bStream.toByteArray())))
# The getter for the zoom property.
def getModel(self):
return self._model
# The setter for the model property. We also make define this as a Qt slot
# which can be connected to Qt signals in Qt Designer.
#QtCore.pyqtSlot(QtCore.QString)
def setModel(self, model):
# print "new model", model
# Set QLabel text
self.setText(model)
# Don't do anything if nothing has changed.
if self._model == model:
return
# Remember the new model level.
self._model = model
# Emit the Qt signal to say that the model level has changed.
self.modelChanged.emit(model)
# The resetter for the model property.
def resetModel(self):
self.setModel(None)
# Define the model property. Changing the value of this in Qt Designer's
# property editor causes the model to change dynamically.
model = QtCore.pyqtProperty(QtCore.QString, getModel, setModel, resetModel)
# Display the custom widget if the script is being run directly from the
# command line.
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
demo = WidgetLabel()
demo.show()
sys.exit(app.exec_())

How to re-implement Ui_MainWindow generated by Qt

I've created a interface in Qt as .ui file and then converted it to a python file. Then, I wanted to add some functionality to the components such as radio button, etc. For doing so, I tried to re-implement the class from Qt and add my events. But it gives the following error:
self.radioButton_2.toggled.connect(self.radioButton2Clicked)
NameError: name 'self' is not defined
My first question is whether this is the correct/proper way to deal with classes generated by Qt? And second, why do I get the error?
My code is here:
import sys
from PySide import QtCore, QtGui
from InterfaceClass_Test01 import Ui_MainWindow
class MainInterface(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainInterface, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def setupUi(self, MainWindow):
super(MainInterface, self).setupUi(parent, MainWindow)
self.radioButton.toggled.connect(self.radioButtonClicked)
self.radioButton_2.toggled.connect(self.radioButton2Clicked)
self.radioButton_3.toggled.connect(self.radioButton3Clicked)
def radioButton3Clicked(self, enabled):
pass
def radioButton2Clicked(self, enabled):
pass
def radioButtonClicked(self, enabled):
pass
The generated files are a little unintuitive. The UI class is just a simple wrapper, and is not a sub-class of your top-level widget from Qt Designer (as you might expect).
Instead, the UI class has a setupUi method that takes an instance of your top-level class. This method will add all the widgets from Qt Designer and make them attributes of the passed in instance (which would normally be self). The attribute names are taken from the objectName property in Qt Designer. It is a good idea to reset the default names given by Qt to more readable ones so that they are easy to refer to later. (And don't forget to re-generate the UI module after you've made your changes!)
The module that imports the UI should end up looking like this:
import sys
from PySide import QtCore, QtGui
from InterfaceClass_Test01 import Ui_MainWindow
class MainInterface(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainInterface, self).__init__(parent)
# inherited from Ui_MainWindow
self.setupUi(self)
self.radioButton.toggled.connect(self.radioButtonClicked)
self.radioButton_2.toggled.connect(self.radioButton2Clicked)
self.radioButton_3.toggled.connect(self.radioButton3Clicked)
def radioButton3Clicked(self, enabled):
pass
def radioButton2Clicked(self, enabled):
pass
def radioButtonClicked(self, enabled):
pass

QStyledItemDelegate with QComboBox: Shows Index and not Text

I have a QStyledItemDelegate for a table. In one cell I have a QComboBox created through the delegate's createEditor. I add some items to the combobox listing via self.addItem("an item"); however, when I go into the table and actually select the items I have added, they get replaced with index values starting from 0.
How can I have the QComboBox display the actual text I added in addItem instead of the index they're getting stored in?
Here is a small standalone example of my problem:
import sys
from PySide import QtCore, QtGui, QtSql
class EditDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent=None):
super(EditDelegate, self).__init__(parent)
def createEditor(self, parent, option, index):
editor = TheEditor(parent)
return editor
class TheEditor(QtGui.QComboBox):
def __init__(self, parent=None):
super(TheEditor, self).__init__(parent)
self.addItem("Item 1")
self.addItem("Item 2")
self.addItem("Item 3")
self.setEditable(True)
class TheTable(QtGui.QTableWidget):
def __init__(self, columns, parent=None):
super(TheTable, self).__init__(parent)
self.setItemDelegate(EditDelegate())
self.setEditTriggers(QtGui.QAbstractItemView.AllEditTriggers)
self.setColumnCount(1)
self.setRowCount(1)
self.setHorizontalHeaderLabels(["QCombo"])
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setCentralWidget(TheTable(self))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()
Updating PySide to the latest version resolves the issue.
It seems that the default combobox delegate is messing up with the data it receives.You could have fixed that with a custom delegate to paint the correct data. But since you have already solved it, Congratulations!.

Categories

Resources