Find and select a row in QTableView - python

I have a QWidget which contains a QTableView..A find dialog is poped up after pressing Ctrl+F.
I want to select the row (rows) which contain the text entered in find dialog in their first column.
I have this code but the problem is that it does not do the row selecting, please guide me whether i have a good approach for finding matches and selecting the corresponding row in the table. Problem is that the following code can not find matches and select them.
class Widget(QWidget):
def __init__(self,md,parent=None):
QWidget.__init__(self,parent)
layout=QVBoxLayout(self)
# initially construct the visible table
self.table = QTableView()
self.table.horizontalHeader().setStretchLastSection(True) # uncomment this if the last column shall cover the rest
self.table.show()
# set black grid lines
self.setStyleSheet("gridline-color: rgb(39, 42, 49)")
# construct the Qt model belonging to the visible table
model = NvmQtModel(md)
self.table.setModel(model)
self.table.resizeRowsToContents()
self.table.resizeColumnsToContents()
# set the shortcut ctrl+F for find in menu
shortcut = QShortcut(QKeySequence('Ctrl+f'), self)
shortcut.activated.connect(self.handleFind)
# shows and handles the find dialog
def handleFind(self):
findDialog = QDialog()
findDialog.setWindowTitle("Find")
grid = QGridLayout()
findDialog.setLayout(grid)
findLabel = QLabel("Find what", findDialog)
grid.addWidget(findLabel,1,0)
findField = QLineEdit(findDialog)
grid.addWidget(findField,1,1)
findButton = QPushButton("Find", findDialog)
grid.addWidget(findButton,2,1)
findButton.clicked.connect(
lambda: self.find(findField.text()))
print("Hereee1")
findDialog.exec_()
# find function: search in the first column of the table
def find(self, text, column=0):
print("Hereee2")
model = self.table.model()
start = model.index(0, column)
matches = model.match(
start, Qt.DisplayRole,
text, 1, Qt.MatchContains)
print(text)
if matches:
index = matches[0]
# index.row(), index.column()
self.table.selectionModel().select(
index, QtGui.QItemSelectionModel.Select)
print("Hereee3")

Related

PyQt5. Row editing

I made PyQt5 APP which create SQlite3 DB and show data from it in QTableView widget.
I have a problem with editing rows in a widget. There are 3 buttons "Add", "Change" and "Delete" that should delete, modify and add new rows to the widget, as well as edit the database itself, but the buttons do not work properly.
"Add" button - after clicking add button, when all new data inputted and Enter clicked, all values is gone and "!" symbol is showing. I need press Add button, input new data in cells, click Enter and all data must save in SQL DB and displayed in QTableView widget in live time.
"Change" button - when change clicked and Enter pressed after cell editing, all data gone. All data must be save in real time is SQL DB after change button press.
3)"Delete" button - this button don't delete row. There is no error, no any answer from app.
How to program the buttons correctly ?
Do I need to use a delegate?
Which programming approach is more preferable when working with a graphical interface and SQL database?
I made a reproducible example. At startup, a database with 1 table and 2 rows will be created.
import sys, os, sqlite3
from datetime import datetime
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtCore import *
CONFIG_NAME = 'config.ini'
DB_NAME = 'nsi.db'
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__()
self.window_pref()
self.show_widgets()
def window_pref(self):
self.setWindowTitle('PyQt5 APP')
self.def_width = 800
self.def_height = 400
self.def_size = self.setMinimumSize(self.def_width, self.def_height)
def show_widgets(self):
self.createConnection()
self.fillDB()
self.setupMainWidgets()
def createConnection(self):
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(DB_NAME)
if not db.open():
QMessageBox.warning(self, 'PyQt5 APP',
'Error:{}'.format(db.lastError().text()))
sys.exit(1)
def fillDB(self):
query = QSqlQuery()
query.exec_("""\
CREATE TABLE sprav (
id_nsi INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
nsi_name TEXT UNIQUE NOT NULL,
file_date TEXT NOT NULL,
file_name TEXT NOT NULL)
""")
query.prepare("""\
INSERT INTO sprav (nsi_name, file_date, file_name)VALUES (?, ?, ?)
""")
sample_list = (('nsi1', 'january', 'file1'), ('nsi2', 'may', 'file2'))
for i in sample_list:
query.addBindValue(i[0])
query.addBindValue(i[1])
query.addBindValue(i[2])
query.exec_()
def setupMainWidgets(self):
mw_widget = QWidget()
main_panel = QHBoxLayout(mw_widget)
# SQL Table
self.modelSql = QSqlTableModel()
self.modelSql.setTable('sprav')
self.modelSql.setQuery(QSqlQuery(
'SELECT nsi_name, file_date, file_name FROM sprav'))
self.modelSql.setHeaderData(self.modelSql.fieldIndex('nsi_name'),
Qt.Horizontal, 'Name')
self.modelSql.setHeaderData(self.modelSql.fieldIndex('file_date'),
Qt.Horizontal, 'Date')
self.modelSql.setHeaderData(self.modelSql.fieldIndex('file_name'),
Qt.Horizontal, 'File')
self.modelSql.setEditStrategy(QSqlTableModel.OnFieldChange)
self.modelSql.select()
# QTableView()
self.table_view = QTableView()
self.table_view.setSelectionBehavior(1)
self.table_view.setAlternatingRowColors(True)
self.table_view.setModel(self.modelSql)
self.table_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
main_panel.addWidget(self.table_view)
# QVBoxLayout()
right_panel = QVBoxLayout()
line = QFrame()
line.setFrameShape(QFrame.HLine)
self.add_record = QPushButton('Add', self)
self.add_record.clicked.connect(self.addRecord)
self.change_record = QPushButton('Change', self)
self.change_record.clicked.connect(self.changeRecord)
self.delete_record = QPushButton('Delete', self)
self.delete_record.clicked.connect(self.delRecord)
right_panel.addSpacing(20)
right_panel.addWidget(line)
right_panel.addWidget(self.add_record)
right_panel.addWidget(self.change_record)
right_panel.addWidget(self.delete_record)
right_panel.addStretch()
main_panel.addLayout(right_panel)
self.setCentralWidget(mw_widget)
def addRecord(self):
row = self.modelSql.rowCount()
self.modelSql.insertRow(row)
index = self.modelSql.index(row, 0)
self.table_view.setCurrentIndex(index)
self.table_view.edit(index)
def delRecord(self):
cur_item = self.table_view.selectedIndexes()
for index in cur_item:
self.modelSql.removeRow(index.row())
self.modelSql.select()
def changeRecord(self):
self.table_view.edit(self.table_view.currentIndex())
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
There are two problems with your code.
First of all, if you use QSqlTableModel, you should not call setQuery():
This function simply calls QSqlQueryModel::setQuery(query). You should normally not call it on a QSqlTableModel. Instead, use setTable(), setSort(), setFilter(), etc., to set up the query.
This is the main reason for which your row addition/deletion/editing didn't work: the model became partially invalid, and any submission was discarded because columns didn't match the records of the table model, which is very important also because the model requires an incremental key that QSqlTableModel is able to use properly on its own.
Remove the setQuery() from your code, and consider that if you did that only to hide a column, then you just have to hide that column:
self.table_view.setColumnHidden(0, True)
Obviously, you have to keep in mind that all column indexes you will use, will now start from 1, since the model also includes the id:
def addRecord(self):
# ...
index = self.modelSql.index(row, 1)
self.table_view.setCurrentIndex(index)
self.table_view.edit(index)
The other problem was the deletion of rows: even after fixing what described above, the number of rows and its order would have been wrong:
when you cycle through the selectedIndexes() you're getting the same row for each selected item: since you used the SelectRows selection behavior, the for loop would have called removeRow() three times the same row, for each selected row;
removal of indexes should always be in reverse, sorted order; consider if you try to remove row 0 and 1: the first iteration would remove row 0, but at that point the previous row 1 would have become the new row 0, so the next iteration would actually delete the row that previously was the third;
The solution is to have a sorted list of unique row numbers, and cycle through them in reversed order:
def delRecord(self):
# create a set of unique row numbers
rows = set([i.row() for i in self.table_view.selectedIndexes()])
# cycle through them in reversed sorting order
for row in sorted(rows, reverse=True):
self.modelSql.removeRow(row)
self.modelSql.select()
Remember to call select() (I know you did, but better safe than sorry), as explained in the documentation about removeRows():
Deletions are submitted immediately to the database. The model retains a blank row for successfully deleted row until refreshed with select().
Unrelated notes: 1. avoid unnecessary and confusing imports: since you're already importing QtWidgets with wildcard, there's no point for from PyQt5 import QtWidgets; you either import the submodule, or its classes; 2. setMinimumSize returns nothing, so self.def_size will be None; if you want to keep a variable for the default size, use self.def_size = QSize(self.def_width, self.def_height) then self.setMinimumSize(self.def_size); 3. do not use sys.exit() inside a Qt app (and from a QWidget class), instead use QApplication.exit(1); 4. use more verbose names that also clarify their type: you have almost identical names for buttons and functions (add_record and addRecord), which is a poor choice in naming (also considering the different writing style): a better choice would be to name the button like add_record_btn, or addRecordBtn to follow the Qt convention;

wxPython - DropSource.SetData() workes with string but not with list

I want to drag and drop a list item to another list. Both lists got two columns. I got it working with drag and dropping the value from the first column to the other list. But drag and drop bot values from column 0 and 1 to the other list wont work.
How do i use DropSource.SetData() with a list?
Here is the part of my Code:
def OnDragInit(self, event):
#text = self.lst1.GetItemText(event.GetIndex(),0)
#tobj = wx.TextDataObject(text) #Doesnt work with a list
# With the above two lines everything is working fine!
# Error here
text = []
text.append(self.lst1.GetItemText(event.GetIndex(),0))
text.append(self.lst1.GetItemText(event.GetIndex(),1))
src = wx.DropSource(self.lst1)
src.SetData(text)
src.DoDragDrop(True)
self.lst1.DeleteItem(event.GetIndex())
Here is the error message:
TypeError: DropSource.SetData(): argument 1 has unexpected type 'list'
I suspect that you are using a wx.TextDropTarget with a wx.TextDataObject and what you are passing is clearly a list.
You will need to create custom data object, then serialise the list on drag and de-serialise it on drop.
To serialise/de-serialise the list you can use pickle or marshal and I'm led to believe that you could use json as well but I didn't really look into that.
The coding for a list drag & drop can get quite complicated, so I've put this code together for you to visualise how you might implement it.
The code is commented, so hopefully you will be able to see what is what.
It is slightly more complicated than it has to be, as I have allowed for dragging and dropping from/to lists with an unequal number of columns.
Also note, that you can drag and drop onto the same list.
import wx
import pickle
#import marshal
class MyTarget(wx.PyDropTarget):
def __init__(self, object):
wx.DropTarget.__init__(self)
self.object = object
# specify the type of data to accept
self.data = wx.CustomDataObject("ListCtrlItems")
self.SetDataObject(self.data)
# Called when OnDrop returns True.
def OnData(self, x, y, opt):
# Find insertion point in the target.
index, flags = self.object.HitTest((x, y))
if self.GetData():
# unpickle data
listdata = self.data.GetData()
dropped_list = pickle.loads(listdata)
#dropped_list = marshal.loads(listdata)
if index == -1: # if not inserting, set index to the end of the listctrl
index = self.object.GetItemCount()
#Insert at drop point
for row in dropped_list:
self.object.InsertItem(index, row[0]) #Insert item
cols = self.object.GetColumnCount()
for pos in range(1,cols):
try:
self.object.SetItem(index, pos, row[pos]) #Add extra columns data
except Exception as e: # run out of columns in target
pass
index +=1
return True
class Mywin(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self, parent, wx.ID_ANY, title,size= (600,-1))
panel = wx.Panel(self)
box = wx.BoxSizer(wx.HORIZONTAL)
self.listCtrl1 = wx.ListCtrl(panel, -1, style = wx.LC_REPORT|wx.LC_HRULES)
self.listCtrl1.InsertColumn(0, "Item0")
self.listCtrl1.SetColumnWidth(0,100)
self.listCtrl1.InsertColumn(1, "Item1")
self.listCtrl1.SetColumnWidth(1,100)
self.listCtrl2 = wx.ListCtrl(panel, -1, style = wx.LC_REPORT|wx.LC_VRULES|wx.LC_HRULES)
self.listCtrl2.InsertColumn(0, "Item0")
self.listCtrl2.SetColumnWidth(0,100)
self.listCtrl2.InsertColumn(1, "Item1")
self.listCtrl2.SetColumnWidth(0,100)
self.listCtrl2.InsertColumn(2, "Item2")
self.delete = wx.CheckBox(panel, wx.ID_ANY, "Delete on move")
self.delete.SetToolTip("Delete original item when dragged & dropped")
self.delete.SetValue(True)
#load sample data
data = [["abc",1],["def",2],["ghi",3]]
for i in data:
self.listCtrl1.Append((i))
data = [["ABC",1,"first"],["DEF",2,"second"],["GHI",3,"third"]]
for i in data:
self.listCtrl2.Append((i))
#Target Left
tl = MyTarget(self.listCtrl1)
self.listCtrl1.SetDropTarget(tl)
#Target Right
tr = MyTarget(self.listCtrl2)
self.listCtrl2.SetDropTarget(tr)
self.listCtrl1.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDrag)
self.listCtrl2.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDrag)
box.Add(self.listCtrl1, 0, wx.EXPAND)
box.Add(self.listCtrl2, 0, wx.EXPAND)
box.Add(self.delete, 0, wx.ALIGN_TOP)
panel.SetSizer(box)
panel.Fit()
self.Centre()
self.Show(True)
def OnDrag(self, event):
#create a data object for drag-and-drop
object = event.GetEventObject() # listCtrl1 or listCtrl2
list_data = []
idx = -1
while True: # find all the selected items and put them in a list
idx = object.GetNextSelected(idx)
if idx == -1:
break
item_data = []
for item in range(object.GetColumnCount()): # get data from all columns
item_data.append(object.GetItem(idx, item).GetText())
list_data.append(item_data)
# Pickle the items list.
pickle_data = pickle.dumps(list_data)
#pickle_data = marshal.dumps(list_data)
# create custom data object
cdataobj = wx.CustomDataObject("ListCtrlItems")
cdataobj.SetData(pickle_data)
# Now make a data object for the item list.
data = wx.DataObjectComposite()
data.Add(cdataobj)
# Create drop source and begin drag-and-drop.
dropSource = wx.DropSource(object)
dropSource.SetData(data)
result = dropSource.DoDragDrop(True)
# delete dropped items from source list
if self.delete.GetValue(): # Is delete checkbox ticked
if result == wx.DragCopy: # Was the drag and drop successful
while True:
#For this small sample always start at the beginning (-1)
idx = object.GetNextSelected(-1)
if idx == -1: #No more selected items
break
object.DeleteItem(idx)
demo = wx.App()
Mywin(None,'Drag & Drop ListCtrl Demo')
demo.MainLoop()
In answer to your comment about the result coming back from the DoDragDrop:
The DragResult enumeration provides the following values:
Description Value
DragError Error prevented the D&D operation from completing.
DragNone Drag target didn’t accept the data.
DragCopy The data was successfully copied.
DragMove The data was successfully moved (MSW only).
DragLink Operation is a drag-link.
DragCancel The operation was cancelled by user (not an error).
DoDragDrop(self, flags=Drag_CopyOnly) Starts the drag-and-drop
operation which will terminate when the user releases the mouse.
Call this in response to a mouse button press, for example.
Parameters: flags (int) – If wx.Drag_AllowMove is included in the
flags, data may be moved and not only copied as is the case for the
default wx.Drag_CopyOnly . If wx.Drag_DefaultMove is specified (which
includes the previous flag), moving is not only possible but becomes
the default operation. Return type: wx.DragResult Returns: The
operation requested by the user, may be wx.DragCopy , wx.DragMove ,
wx.DragLink , wx.DragCancel or wx.DragNone if an error occurred.

Check row by row in QTableWidget to affect QCombobox

I have a QTableWidget, 2 Columns in which the first column contains the name of items while the second column is populated with qcomboboxes (created using cellwidgets) which contains a list of color options (eg. "", RED, BLUE, GREEN)
For example, in the following table, items denoted with * has a variable called color_set, I am trying to achieve as follows:
it will lookup the names of items in the scene
then it will go through a function to check and see if the item
shirt_01 contains a variable called color_set
If the variable exists, say if it is RED, it will then lookup the list of options in the combobox and display RED as the value
If the variable does not exists, it will displays a blank field instead
this function/values populate the table with information before the ui is launched, it is not a button clicked function etc.
Name of items | Color Options
shirt_01* | RED
shirt_02 |
pants* | GREEN
However, instead of getting my ui to output as above, I got the following result:
Name of items | Color Options
shirt_01* | RED
shirt_02 | RED
pants* | GREEN
For some reasons, the shirt_02 seems to got 'tangled' and displayed the same result as shirt_01. And I think it could possibly be due to how my logic was written but I could not figure it out or because I keep tying my output result to self.color_combobox as my code initially checks through the first column then find the matching text in the second column.
But I keep getting error at the line - item_name has no attribute .text()
import maya.cmds as cmds
from PyQt4 import QtGui, QtCore
import json
def get_all_mesh():
all_mesh = cmds.listRelatives(cmds.ls(type = 'mesh'), parent=True)
return all_mesh
def read_json(node_name):
# checks if the node_name exists in the json file
with open('/Desktop/car_colors.json') as data_file:
data = json.load(data_file)
items = set()
for index, name in enumerate(data):
# if the name is in the json, it will states the color
if node_name in name:
for item in (data[name]):
#print "{0} - {1}".format(name, item)
items.add(item)
return items
def attrToPy(objAttr):
stringAttrData = str(cmds.getAttr(objAttr))
loadedData = cPickle.loads(stringAttrData)
return loadedData
class testTableView(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.setWindowTitle('Color Test')
self.setModal(False)
self.all_mesh = get_all_mesh()
# Build the GUI
self.init_ui()
self.populate_data()
def init_ui(self):
# Table setup
self.mesh_table = QtGui.QTableWidget()
self.mesh_table.setRowCount(len(self.all_mesh))
self.mesh_table.setColumnCount(3)
self.mesh_table.setHorizontalHeaderLabels(['Mesh Found', 'Color for Mesh'])
self.md_insert_color_btn = QtGui.QPushButton('Apply color')
# Layout
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.mesh_table)
self.layout.addWidget(self.md_insert_color_btn)
self.setLayout(self.layout)
def populate_data(self):
geo_name = self.all_mesh
for row_index, geo_item in enumerate(geo_name):
new_item = QtGui.QTableWidgetItem(geo_item)
# Add in each and every mesh found in scene and append them into rows
self.mesh_table.setItem(row_index, 0, new_item)
geo_exclude_num = ''.join(i for i in geo_item if not i.isdigit())
color_list = read_json(geo_exclude_num)
color_list.add("")
# Insert in the color
self.color_combobox = QtGui.QComboBox()
#color_list = get_color()
self.color_combobox.addItems(list(sorted(color_list)))
self.mesh_table.setCellWidget(row_index, 1, self.color_combobox)
self.color_value_to_combobox()
def color_value_to_combobox(self):
obj_nodes = get_all_mesh()
for node in obj_nodes:
# This attribute is stored in the Extra Attributes
objAttr = '%s.pyPickle'%node
storedData = attrToPy(objAttr)
if 'color_set' in storedData:
color_variant = storedData['color_set']
combo_index = self.color_combobox.findText(color_variant, QtCore.Qt.MatchFixedString)
self.color_combobox.setCurrentIndex(0 if combo_index < 0 else combo_index)
# To opent the dialog window
dialog = testTableView()
dialog.show()
Is there anyway in which I can make my ui to check for the list of item in scene, tallies which name and combobox it should go (in terms of which rows/columns)? Otherwise what other ways can i approach?
EDIT: This is the attachment that I did it quick - Link
If you run the UI, you will notice that pCube1 has the color "red" while pCube2 is "blue", however both the comboboxes will turn up as blue instead
Ok, so the problem was in color_value_to_combobox. Instead of setting the combobox for ALL objects, you need to specify what object and combobox the function needs to work on. The major flaw was that you were using self.color_combobox to set with every time, so every object would keep setting the same combobox! It needs to be more generic.
Anyways, I just had to change the end of populate_data and color_value_to_combobox. Now cube1 shows red, and cube2 shows blue.
import maya.cmds as cmds
from PyQt4 import QtGui, QtCore
import json
import cPickle
def get_all_mesh():
all_mesh = cmds.listRelatives(cmds.ls(type = 'mesh'), parent=True)
return all_mesh
def read_json(node_name):
# checks if the node_name exists in the json file
with open('/Desktop/car_colors.json') as data_file:
data = json.load(data_file)
items = set()
for index, name in enumerate(data):
# if the name is in the json, it will states the color
if node_name in name:
for item in (data[name]):
#print "{0} - {1}".format(name, item)
items.add(item)
return items
def attrToPy(objAttr):
stringAttrData = str(cmds.getAttr(objAttr))
loadedData = cPickle.loads(stringAttrData)
return loadedData
class testTableView(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.setWindowTitle('Color Test')
#self.setModal(False)
self.all_mesh = get_all_mesh()
# Build the GUI
self.init_ui()
self.populate_data()
def init_ui(self):
# Table setup
self.mesh_table = QtGui.QTableWidget()
self.mesh_table.setRowCount(len(self.all_mesh))
self.mesh_table.setColumnCount(3)
self.mesh_table.setHorizontalHeaderLabels(['Mesh Found', 'Color for Mesh'])
self.md_insert_color_btn = QtGui.QPushButton('Apply color')
# Layout
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.mesh_table)
self.layout.addWidget(self.md_insert_color_btn)
self.setLayout(self.layout)
def populate_data(self):
geo_name = self.all_mesh
for row_index, geo_item in enumerate(geo_name):
new_item = QtGui.QTableWidgetItem(geo_item)
# Add in each and every mesh found in scene and append them into rows
self.mesh_table.setItem(row_index, 0, new_item)
geo_exclude_num = ''.join(i for i in geo_item if not i.isdigit())
color_list = read_json(geo_exclude_num)
color_list.add("")
# Insert in the color
color_combobox = QtGui.QComboBox()
#color_list = get_color()
color_combobox.addItems(list(sorted(color_list)))
self.mesh_table.setCellWidget(row_index, 1, color_combobox)
self.color_value_to_combobox(geo_item, color_combobox) # Pass the object and combobox you want to set
# This use to work on all mesh objects, but only edits a single combobox. This is wrong!
def color_value_to_combobox(self, node, combobox):
# This attribute is stored in the Extra Attributes
objAttr = '%s.pyPickle'%node
storedData = attrToPy(objAttr)
if 'color_set' in storedData:
color_variant = storedData['color_set']
combo_index = combobox.findText(color_variant, QtCore.Qt.MatchFixedString)
combobox.setCurrentIndex(0 if combo_index < 0 else combo_index) # Needs to work on the proper combobox!
# To opent the dialog window
dialog = testTableView()
dialog.show()
If the color variable doesn't exist, your code needs to explicitly set the color combo to a blank item:
def populate_data(self):
geo_name = self.all_mesh
for row_index, geo_item in enumerate(geo_name):
new_item = QtGui.QTableWidgetItem(geo_item)
# Add in each and every mesh found in scene and append them into rows
self.mesh_table.setItem(row_index, 0, new_item)
geo_exclude_num = ''.join(i for i in geo_item if not i.isdigit())
color_list = read_json(geo_exclude_num)
color_list.add("")
# Insert in the color
color_combobox = QtGui.QComboBox()
#color_list = get_color()
color_combobox.addItems(list(sorted(color_list)))
self.mesh_table.setCellWidget(row_index, 1, color_combobox)
# This attribute is stored in the Extra Attributes
objAttr = '%s.pyPickle' % geo_item
storedData = attrToPy(objAttr)
if 'color_set' in storedData:
color_variant = storedData['color_set']
else:
color_variant = ''
combo_index = color_combobox.findText(color_variant, QtCore.Qt.MatchFixedString)
color_combobox.setCurrentIndex(0 if combo_index < 0 else combo_index)
(NB: for this to work, you obviously must make sure the first entry in the combo-boxes is a blank item).
If you look at the code you posted, it doesn't show the loop over the rows to put the combo boxes in each cell, but I will assume there is one otherwise it wouldn't work at all.
What you should do is one of two things:
- if you have a separate loop to create your combo boxes then in that loop don't create a combo box if the item does not have colors; then in the second loop you check if the cell has a combo box and only if it does do you configure it.
- Second option is to move the first loop into the rows loop, ie you as loop through each row you determine if the item has colors, and only if it does, do you create the combo box and configure it; if no colors, just leave that sell empty.

Items are not adding in correctly in QTableWidget in Maya

I am trying to create a table, where it has 2 columns and several rows.
Column1 will be listing all the available mesh/geos in the scene while Column2 will be in the form of combo box per cell where it contains several options - depending on the mesh/geos from Column1, it will lists different shader options in the combobox as it will be reading off from a file. Meaning to say in the table, each item is on a per-row basis.
I am currently having issues with populating the list of mesh/geos into Column1. Suppose my scene has 5 geos - pCube1, pCube2, pCube3, pCube4, pCube5, in my table, I would be expecting the Column0 of its 5 rows to be populated with pCube#, however instead of that, I got pCube5 as my output result instead.
Please see the following code:
from PyQt4 import QtGui, QtCore
from functools import partial
import maya.cmds as cmds
class combo_box( QtGui.QComboBox ):
# For combox
def __init__( self, *args, **kwargs ):
super( combo_box, self ).__init__( *args, **kwargs)
def get_all_geos():
all_geos = cmds.ls(type='mesh')
return all_geos
class TestTable( QtGui.QWidget ):
def __init__( self, parent=None ):
QtGui.QWidget.__init__( self, parent )
self.setLayout( QtGui.QVBoxLayout() )
self.resize( 600, 300 )
self.myTable = QtGui.QTableWidget()
self.myTable.setColumnCount( 2 )
rowCount = len(get_all_geos())
self.myTable.setRowCount(rowCount)
self.setTable()
self.layout().addWidget(self.myTable)
self.myTable.cellChanged.connect(self.update)
def setTable(self):
# Adding the list of mesh found in scene into first column
for geo in get_all_geos():
item = cmds.listRelatives(geo, parent=True)[0]
for i in range(0, self.myTable.rowCount()):
# instead of being populated with the list of items, I got the same name for the entire column
self.myTable.setItem(i, 0, QtGui.QTableWidgetItem(item))
# sets the combobox into the second column
box = combo_box()
nameList = ("test1","test2","test3")
box.addItems(nameList)
self.myTable.setCellWidget(i,1,box)
box.currentIndexChanged.connect(partial(self.tmp, i))
def tmp(self, rowIndex, comboBoxIndex):
item = "item " + str(comboBoxIndex)
self.myTable.setItem(rowIndex, 2, QtGui.QTableWidgetItem(item))
if __name__ == "__main__":
tableView = TestTable()
tableView.show()
In my setTable function, the item is not being processed correctly? when I am trying to add it into the QTableWidget. Can someone advise?
Additionally, if anyone could answers, does the format I have used, would it be applicable for the scenario I am trying to achieve as I mentioned at the start of the post?
In your setTable() method, you are looping through the geometries, then you are looping through the rows. Since each geometry represents a row you only really need to loop through them and remove the other loop.
Modifying it like so fixes the output:
def setTable(self):
# Adding the list of mesh found in scene into first column
geos = get_all_geos()
for i in range(0, len(geos)):
item = cmds.listRelatives(geos[i], parent=True)[0]
# instead of being populated with the list of items, I got the same name for the entire column
self.myTable.setItem(i, 0, QtGui.QTableWidgetItem(item))
# sets the combobox into the second column
box = combo_box()
nameList = ("test1","test2","test3")
box.addItems(nameList)
self.myTable.setCellWidget(i,1,box)
box.currentIndexChanged.connect(partial(self.tmp, i))
The reason it was failing was because your second loop kept overriding the rows with the last geo in the list.

PyGTK TreeView showing blank rows from ListStore

I'm trying to show several rows from database in a TreeView but all I am getting are some dummy rows as you can see in the image below.
class SettingsDialog(gtk.Dialog):
def __init__(self):
gtk.Dialog.__init__(self, "Server Settings", self, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
# Instantiate ServerManager
self.server_manager = ServerManager()
# Create TreeStore for Server list
self.liststore = gtk.ListStore(str, str)
self.treeview = gtk.TreeView(self.liststore)
# Create TreeViewColumns to display data
cell = gtk.CellRendererText()
col = gtk.TreeViewColumn("Name")
col.pack_start(cell, True)
self.treeview.append_column(col)
cell = gtk.CellRendererText()
col = gtk.TreeViewColumn("URL")
col.pack_start(cell, True)
self.treeview.append_column(col)
self.vbox.pack_start(self.treeview)
self.resize(500,350)
self.set_position(gtk.WIN_POS_CENTER)
self.show_all()
self.load_server_list()
def load_server_list(self):
self.liststore.clear()
servers = self.server_manager.list()
for name, url in servers.iteritems():
self.liststore.append([name, url])
self.show_all()
Data returned from self.server_manager.list() is valid an added to the list store perfectly. There seems to be something wrong with the CellRenderers but I wasn't able to find the error.
You have to set an attribute mapping on the column. For example, the cellrenderer's text attribute value will be displayed in the treeview cell. It is taken from the values on the data model (self.liststore). The column number on the model where the value is taken from is specified in the attribute mapping.
## Take value for *text* attribute of the cell renderer from the model's 3rd column
col = gtk.TreeViewColumn(title, cellrenderer, text=2)

Categories