I am trying to code the following: Two Columns. One contains a itemId, the other one contains a typeId. I want to render the itemId only when the typeId equals a specific value.
class IDRenderer(gtk.CellRendererText):
def __init__(self):
gtk.CellRendererText.__init__(self)
def do_render(self,window, widget, background_area, cell_area, expose_area, flags):
if ----} Condition to ask for value of the typeId - Cell {-----:
gtk.CellRendererText.do_render(self, window, widget, background_area, cell_area,
expose_area, flags)
gobject.type_register(IDRenderer)
I don't know how to get the iter of the currently rendered row which i need to determine the value of the typeId. Is this even possible?
I now found out, thanks to a nice guy on #pygtk on gimpIRC:
You can do that, with binding so called cell data functions to the corresponding gtk.TreeViewColumn as done here in this example
def renderId(celllayout, cell, model, iter):
if model.get_value(iter,1) == 3:
cell.set_property('visible',True)
else:
cell.set_property('visible',False)
treeviewcolumn = gtk.TreeViewColumn()
renderer = gtk.CellRendererText()
treeviewcolumn.add_attribute(renderer,'text',0)
treeviewcolumn.set_cell_data_func(renderer,renderId)
I ommited some code relevant to render a complete treeview, but i think it shows what i wanted to do and how to do it.
The column renderes the value in the first column (0) of the model only if the value in the second modelcolumn (1) equals 3
I hope this could help someone some time.
It's not possible as far as I know. You need to use properties of the custom renderer which will be set automatically by the code calling the rendering function. (Like the text property of CellRendererText -- the rendering code doesn't get the text from the tree model, but the tree model sets the text property of the renderer before calling the rendering code.)
Related
I'm using pyqt5 and python 3.6.
I am confused about index functions in QModel/TreeView and ask here to clear my knot in brain. Some threads confused me more because they are very special. My question is more or less a basic one.
I have a model (based on Uludag's great tutorials) with data and did set it to my tree:
treeView = QTreeView()
treeView.show()
treeView.setModel(model)
#additional code
treeView.clicked.connect(treeViewStructure_clicked)
Now I have to prepare further action in the program based on the selected item. I can get the item by
def treeViewStructure_clicked(self):
#get the row of the click and print
index = treeView.currentIndex()
print(index.row())
print('selected item index at %s with data: %s' % (index.row(), index.data()))
So far so good. But the index is generated from treeView (model doesn't work). How can I connect the selection form the view to the model, so that I have the correct Modelindex and can change data in the model?
And of course, how to write them back that they will get synchronized? In other words, which index do I need for what and where and where do they come from?
I would love to leave a comment but I don't have enough reputation.
Since your implementation of model did not include a method that could return the selected index, I have to get it from pyqt5. Since treeView is a QTreeView, you can:
treeView.selectionModel().selectedIndexes()
the information is from here.
These will return a QModelIndex list, which has an api here.
(note that although there are code examples on this website, it's in C++, but it should still be readable)
In the api, you can get the row and column as well as parent from the QModelIndex object.
Normally, I'd just modify the data via the object returned by treeView.selectionModel().selectedIndexes().
for example:
arr = treeView.selectionModel().selectedIndexes()
index = arr[0]
then just modify index.
but if you MUST use your own model for whatever reason, there is one thing you can do.
Since it extends QAbstractItemModel, you can use the index function.
For example:
arr = treeView.selectionModel().selectedIndexes()
indexTemp = arr[0]
index = model.index(indexTemp.row(), indexTemp.column(), indexTemp.parent())
then modify the value through setData funtion
But as you can see, it's an extra step to get the same object
im using kivy. the what im trying to do is have and 'idea',a slider and a label containing the slider's current value in a row in a grid layout
now getting the layout is fine but getting the label to have a text value the same as the slider's current value is tricky. I'm trying to use string concation to refer to the label with the same number suffix as the slider that it is paired with.
I think the problem im having is that im trying to assign ids on the python side when they normally have to be done on the kv side. It's either that or the fact the ids i'm assigning are strings when kv would normally expect plain text. any help would be appreciated
class ScatterTextWidget(FloatLayout):
def run_me(self):
r=1
main_list=self.ids.main_list
main_list.clear_widgets()
main_list.height=0
for idea in imported_ideas:
main_list.add_widget(Label(text=idea,color=(0,0,0,1),id='idea_label_'+str(r)))
main_list.add_widget(Slider(id='Slider_'+str(r),min=0,max=10,value=5, step=1,on_value_pos=self.slider_slid(self)))
main_list.add_widget(Label(color=(0,0,0,1),id='value_label_'+str(r)))
value_label=self.ids['value_label_'+str(r)] # get this working and then apply the method into slider slid
value_label.text='xxx'
main_list.height+=35
r +=1
button_1=self.ids.button_1
button_1.text='Begin'
button_1.bind(on_press=self.begin)
def slider_slid(self,sender):
s=str(sender.id)
value_label=self.ids['value_label_'+str(s[12:])]
value_label.text=str(sender.value)
value_label=self.ids['value_label_'+str(s[12:])]
KeyError: 'value_label_'
self.ids only collects ids from children in the kv language rule of the widget. It doesn't know about widgets you added via python.
You don't need to use the id though. In this case you could keep e.g. a dictionary of id -> widget keys.
self.keys_dict = {}
for idea in imported_ideas:
new_widget = Label(color=(0,0,0,1),id='value_label_'+str(r)))
main_list.add_widget(new_widget)
self.keys_dict['value_label_' + str(r)] = new_widget
Then later you can access it with self.keys_dict['value_label_' + str(s[12:])] or whatever you like.
I suppose in practice you could also modify the actual ids dictionary in the same way, though I subjectively feel it is preferable to maintain your own dictionary with a name that represents its more specific contents.
I'm writing an exporter for a game my friend and I are making and it involves setting custom properties and tags to objects which are then recognized in the game and dealt with accordingly. Our engine, which is written in C/C++ has been successfully tested with my current version of the export script, and I''m currently working on tidying it up.
The script uses Blender's feature of custom properties to write custom tags to output file. The model typically consists of multiple 'parts' (Blender mesh objects that are parented to form a tree, with one 'parent' and multiple 'child' objects) and some of those parts are simple Blender Empty objects (for only it's X, Y and Z coordinates are needed) with custom properties that mark where things like ship's propulsion (it's a 3D shooter) are placed, or where the flames/explosions appear when ship's been shot. Those empty parts are also parented to either 'root' object or any of it's children. So far it's been working good, I have written a generic Operator class and some extended classes that reside in a panel which set part's properties (pretty handy since you don't have to add those custom properties by hand).
Now I want to speed thing up even more, that is to be able to simply click on an operator of desired type, and it should automatically add it to the scene and parent it to the active/selected object. I know how to do that, but I can't get those operators to change their labels. Basically, what I want is to operator to say 'Bullet point' when an existing empty is selected (I've got that part done), and when there's a mesh object selected to say 'Add bullet point'. So I just need a way to dynamically change operators' labels depending on the context (as the title of the question states clearly :))
This is what I got so far:
class OBJECT_OT_tg_generic (bpy.types.Operator):
bl_label = "Sets Generic Part Type"
bl_idname = "rbm.set_generic_part_type"
OB_TYPE = None
#classmethod
def poll (cls, context):
act = context.active_object
if 'Type' in act.keys ():
if act['Type'] == cls.OB_TYPE:
cls.bl_label = 'foo'
print (cls.bl_label)
# this prints foo but doesn't change the label
return False
return True
def execute (self, context):
# TODO: add code to automatically place empties and parent them to active object
bpy.context.active_object['Type'] = self.OB_TYPE
return{"FINISHED"}
And an example of a subclass:
class OBJECT_OT_tg_bullet_point (OBJECT_OT_tg_generic):
bl_label = "Bullet Point"
bl_idname = "rbm.set_bullet_point"
OB_TYPE = OT_BULLET_POINT
Here's how it looks in Blender:
http://i.imgur.com/46RAS.png
Guess I solved it. When you're adding an operator to a panel, you can do something like this:
def draw (self, context):
layout = self.layout
row = layout.row()
row.operator("foo.bar", text="Whatever you want")
and the "Whatever you want" is going to be your button's label. But what I did was something else. I didn't change the operators' labels, but instead gave them a different icons depending on whether it's a mesh or an empty currently selected/active:
def draw (self, context):
# (...) we're skipping some code here, obviously
act = context.active_object
if act.type == 'MESH':
op_icon = 'ZOOMIN'
else:
op_icon = 'EMPTY_DATA'
row = layout.column(align=True)
row.operator('rbm.set_bullet_point', icon=op_icon)
row.operator('rbm.set_rocket_point', icon=op_icon)
# (...) rest of the code
I have a number of items in a QTreeView. Each item is generated using this class:
class Branch(QStandardItem):
def __init__(self, label, uri = None):
QStandardItem.__init__(self, label)
self.uri = uri
This is my actual tree:
class FileTree(QTreeView):
def __init__(self):
QTreeView.__init__(self)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return:
crawler = self.selectedIndexes()[0].model().item(self.selectedIndexes()[0].row())
print(crawler.uri)
QTreeView.keyPressEvent(self, event)
As you can see, I'm a little unsure as to how to get the uri variable from the selected item. I found that selectedIndexes() returns a model and not the item itself. I'm not sure how to get from one to the other. Trying to get the item number using self.selectedIndexes()[0].row() was a bit of a shot in the dark, but it seems to ignore the various branches in the tree (for instance, it will give me a 0 for the first row in a branch, but won't tell me anything about what branch it's in).
What's the proper way to get the selected item from the QTreeView? Or is there a better way of detecting the spacebar or return keys being hit that would make this easier? There's a severe lack of Python documentation for Qt, so it's really hard to know if I'm ever doing things in a sensical manner.
You are calling the right function, it actually returns a QModelIndexList which is just a typedef for QList<QModelIndex> with the QModelIndex being the data structure that can point to any part of the tree. QModelIndex is not a Model in the sense of Model View Controller (MVC) but an adress of an object in a QAbstractItemModel which is the datastructure under all of Qt's ItemView objects, including your tree. You are actually pretty close, QAbstractModelIndex consists of a row, a column and a parent, which lets it adress any position in a hierarchical data structure. If you use the line
index = self.selectedIndexes()[0]
crawler = index.model().itemFromIndex(index)
you should get to the object that you are looking for.
As for documentation, even though there is no python specific documentation it helps to read through the official Qt documentation, the class hierarchy and functionality is still the same. There is very little C++ specific information in the docs.
Harald's answer didn't work for me, because I'm using a QSqlQueryModel as the model (I got the error {AttributeError}'QSqlQueryModel' object has no attribute 'itemFromIndex').
The below did the trick for me though, to get the 0th column data of the selected row:
dbQueryModel.itemData(treeView.selectedIndexes()[0])
I am using the pyqt4 framework to do some displays for database forms. Unfortunately, I hit a snag while trying to filter and display my database by last name. Assume that the database connection works. Also assume that I have the correct amount of items in my tupleHeader since I use the same initializeModel method for other methods (like the search() function described below, and it works fine.
I call the display() function and it works perfectly fine, but when creating a proxyModel from the sourceModel, and trying to display the proxyModel with my search function, I have empty cells displayed. When I restrict my search so that it filters half my database, it shows that many cells (so most of this is working). But it will not display anything from the database itself.
Below is some of my code:
from PyQt4 import QtGui, QtCore, QtSql
self.caseSensitivity = QtCore.Qt.CaseInsensitive
self.syntax = QtCore.QRegExp.FixedString
def initializeModel(self, model):
model.setTable(self.table)
#model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
b = 0
for a in self.tupleHeader:
model.setHeaderData(b, QtCore.Qt.Horizontal, QtGui.qApp.tr(a))
b += 1
model.select()
def display(self):
'''reads all row data and displays it on a tableview'''
self.connectdb(self.db, self.localhost, self.dbname, self.username, self.password)
model = QtSql.QSqlTableModel()
self.initializeModel(model)
self.view.setModel(model)
self.disconnectdb(self.db)
def search(self, searchQuery):
'''queries database data, filters it, and displays it on a tableview'''
sourceModel = QtSql.QSqlTableModel()
proxyModel = QtGui.QSortFilterProxyModel()
self.initializeModel(sourceModel)
proxyModel.setSourceModel(sourceModel) # allows to edit proxyModel without changing underying model
#searchQuery contains the last name that I am filtering with
regExp = QtCore.QRegExp(searchQuery, self.caseSensitivity, self.syntax)
proxyModel.setFilterRegExp(regExp)
proxyModel.setFilterKeyColumn(2) # this column holds the last names
# self.view contains the table itemview my application uses to display the database
self.view.setModel(proxyModel)
EDIT: I am not interested in keeping this piece of code, I just want to know why it allows the table to show the table's content instead of a bunch of empty cells
print self.proxyModel.filterAcceptsRow(2, self.sourceModel)
Also, if you put in this after the last statement ( self.view.setModel(proxyModel) ), it will show the table, even if it does send an error:
print self.proxyModel.filterAcceptsRow(2, self.sourceModel)
TypeError: QSortFilterProxyModel.filterAcceptsRow(int, QModelIndex): argument 2 has unexpected type 'QSqlTableModel'
It doesn't matter what the arguments are or whether I use filterAcceptsRow ro filterAcceptsColumn, it displays the table. Does this narrow down the problem some?
Thank you for your time searching for this coding error/bug, and happy hunting!
While I could not find the solution to my problem, it solved itself. I am not certain, but I think it was this code snippet that made it work.
self.dbmanip = CoreDB(self.userTableView, self.table)
This was put inside of the SetupUi() method created by the Qt4 Designer. I think either the dbmanip that contained the TableView lost the information from the proxyModel, or (more likely), I may have referenced the wrong table between the proxyModel and the original Model (that created the proxyModel), and then couldn't display because it was calling the cell structure from one table and the actual information from another.
These are all guesses though. Still, problem solved.