I am new to Python scripting and now is trying to design a window interface to save daily expense into EXCEL file.
I have written a QTableWidget to type in the daily expense information but found the error message "AttributeError: 'Window' object has no attribute 'tableWidget'" while saving the data into EXCEL file.
Can anyone give me the hint to move forward?
Below is my code for your reference.
class Window(QtGui.QWidget):
def __init__(self): #Initial a Window frame
super(Window, self).__init__()
self.labels() #insert labels
self.buttons() #insert buttons
self.draglists()
self.table()
.
.
.
def table(self):
table = QtGui.QTableWidget(self)
table.setRowCount(20)
table.setColumnCount(4)
table.resize(450, 300)
table.move(640, 100)
horHeader = ['Date', 'Category', 'Item', 'Expense']
table.setHorizontalHeaderLabels(horHeader)
def savefile(self):
filename = unicode(QtGui.QFileDialog.getSaveFileName(self, 'Save File', '', ".xls(*.xls)"))
wbk = xlwt.Workbook()
sheet = wbk.add_sheet("sheet", cell_overwrite_ok=True)
self.add2(sheet)
wbk.save(filename)
def add2(self, sheet):
for currentColumn in range(self.tableWidget.columnCount()):
for currentRow in range(self.tableWidget.rowCount()):
try:
teext = str(self.tableWidget.item(currentRow, currentColumn)).text()
sheet.write(currentRow, currentColumn, teext)
except AttributeError:
pass
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Window()
ex.setGeometry (50, 50, 1200, 600)
ex.show()
ex.setWindowTitle("Search Engine for Expense")
sys.exit(app.exec_())
You're referencing something you haven't defined yet. Your class Window inherits from QtGui.QWidget, which doesn't have an attribute called tableWidget, yet you reference an attribute called tableWidget here:
def add2(self, sheet):
for currentColumn in range(self.**tableWidget**.columnCount()):
for currentRow in range(self.**tableWidget**.rowCount()):
try:
teext = str(self.**tableWidget**.item(currentRow, currentColumn)).text()
sheet.write(currentRow, currentColumn, teext)
except AttributeError:
pass
Hence the error. If you want self.tableWidget to reference the table you made in table(self), you'd need to make it an instance attribute at the end of the function like so:
def table(self):
table = QtGui.QTableWidget(self)
table.setRowCount(20)
table.setColumnCount(4)
table.resize(450, 300)
table.move(640, 100)
horHeader = ['Date', 'Category', 'Item', 'Expense']
table.setHorizontalHeaderLabels(horHeader)
self.tableWidget = table
That will allow your other functions to access the table and its attributes, including .columnCount() and .rowCount().
Hope that helps.
Related
I have the following code:
uic.loadUi("mainwindow.ui", self)
self.db = QSqlDatabase.addDatabase('QSQLITE')
self.db.setDatabaseName('people.db')
self.db.open()
try:
if self.db.isOpen():
print('DB open')
# self.pushButtonClear.clicked.connect(self.clearFields)
# self.pushButtonSave.clicked.connect(self.insertRowToModel)
self.pushButtonDelete.clicked.connect(self.deleteTableRow)
self.model = QSqlRelationalTableModel(db=self.db)
self.model.setTable("person")
self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
self.group_index = self.model.fieldIndex("GroupId") #foreign key
self.model.setRelation(self.group_index, QSqlRelation("Groups", "GroupId", "GroupName"))
self.model.select()
self.relModel = self.model.relationModel(self.group_index)
self.comboBoxGroup.setModel(self.relModel)
self.comboBoxGroup.setModelColumn(self.relModel.fieldIndex("GroupName"))
self.tableView.setModel(self.model)
self.mapper = QDataWidgetMapper()
self.mapper.setModel(self.model)
self.mapper.setItemDelegate(QSqlRelationalDelegate(self))
self.mapper.addMapping(self.lineEditId, 0)
self.mapper.addMapping(self.lineEditForename, 1)
self.mapper.addMapping(self.lineEditSurename, 2)
self.mapper.addMapping(self.dateEditBirthday, 3)
self.mapper.addMapping(self.lineEditCity, 4)
self.mapper.addMapping(self.comboBoxGroup, self.group_index)
self.mapper.toFirst()
except:
self.db.close()
print('Exception raised')
in line: self.comboBoxGroup.setModelColumn(self.relModel.fieldIndex("GroupName"))
it gives an AttributeError: 'NoneType' object has no attribute 'fieldIndex'
because the self.relModel = self.model.relationModel(self.group_index) results in a None type. It could not be setup. When i enter the column index as a number like this: self.comboBoxGroup.setModelColumn(self.relModel.fieldIndex(1)) the data shows up at least, but the combobox is not filled. What is going wrong here?
I have found the problem:
self.group_index = self.model.fieldIndex("group") #foreign key
field name was incorrect. Now it works but only gives entries where there is
an entry in the group table (inner join)
I am trying to work around autocomplete dropdown combobox which works fine with database. but when i try to fetch data after updating database in runtime I got AttributeError: 'FarmerClass' object has no attribute 'farmer_name'. I tried other available solutions but still error is not solved. as of now, typo and syntax looks fine from the reference code which i was following.
the class which throws error
class FarmerClass(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.veg = []
self.farmer = []
self.buyers = []
self.fetch_data()
self.farmer_name = ttk.Combobox(self)
self.farmer_name['values'] = self.farmer
self.farmer_name.focus()
self.farmer_name.bind('<KeyRelease>', self.check_name)
self.farmer_name.bind('<<ComboboxSelected>>', self.get_data)
self.farmer_name.place(relx=0.028, rely=0.243, width=240, height=24)
self.product = ttk.Combobox(self )
self.product['values'] = self.veg
self.product.bind('<KeyRelease>', self.check_veg)
self.product.place(relx=0.028, rely=0.380,width=240, height=24)
self.buyer = ttk.Combobox(self )
self.buyer['values'] = self.buyers
self.buyer.bind('<KeyRelease>', self.check_buyer)
self.buyer.place(relx=0.028, rely=0.5237,width=240, height=24)
#======================= Entry btn ====================
self.entry_btn = tk.Button(self,Btn_base, text='Entry', command=self.entry_to_bill)
self.entry_btn.place(relx=0.481, rely=0.525,width=134, height=24)
def fetch_data(self, event=None):
cur.execute('SELECT * FROM vegetable ')
for i in cur.fetchall():
self.veg.append(i[0])
cur.execute('SELECT rowid, * FROM farmers ')
for i in cur.fetchall():
self.farmer.append(i[2])
cur.execute('SELECT rowid,name FROM buyers_avail ')
for i in cur.fetchall():
self.buyers.append(i[1])
self.farmer_name.configure(values= self.farmer) # getting error here
self.product.configure(values= self.veg)
self.buyer.configure(values= self.buyers)
def entry_to_bill(self, event=None):
name = self.buyer.get().lower()
cur.execute('SELECT name FROM buyers_avail WHERE name = ? ', [name])
bnames = cur.fetchall()
if bnames:
print(f'buyer {bnames} found')
else:
cur.execute('INSERT INTO buyers_avail (name) VALUES (?)',[name,])
db.commit()
self.buyer.delete(0, tk.END)
self.buyer.focus()
self.fetch_data()
if i try without those lines, farmer_name looks fine, product gets values of buyers and buyer dropdown goes empty.
please ask if anything required related to question.
The fetch_data function accesses farmer_name but you call it before defining farmer_name, which therefore doesn't exist at that point. You need to call fetch_data after defining farmer_name.
I want to build a record from multiple QT widgets and store it in a database. The following script runs without an error but does not store the record. Part of the problem is that I cannot determine the last ID already in the database, but even if I manually set the ID--nothing is written. I've seen examples online using insertRecord, but the QT documentation suggests using insertRow. Please feel free to correct my approach. I'm new to Python and to Qt.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtSql import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
#Make Database
self.db = QSqlDatabase.addDatabase('QSQLITE')
self.db.setDatabaseName('C:/Users/dle/Documents/example1.sqlite')
self.db.open()
self.db.transaction()
self.db.exec_(
'CREATE TABLE t1'
'(id INTEGER PRIMARY KEY, f1 INTEGER NOT NULL, f2 INTEGER NOT NULL, f3 TEXT NOT NULL)'
)
self.db.exec_("INSERT INTO t1 VALUES(1, 10, 20, 'db works fine')")
self.db.commit()
#Create User Interface
self.f1 = QLineEdit()
self.f2 = QLineEdit()
self.f3 = QLineEdit()
self.storeButton = QPushButton("Store")
self.storeButton.clicked.connect(self.doStore)
vlayout = QVBoxLayout()
vlayout.addWidget(self.f1)
vlayout.addWidget(self.f2)
vlayout.addWidget(self.f3)
vlayout.addWidget(self.storeButton)
widget = QWidget()
widget.setLayout(vlayout)
self.setCentralWidget(widget)
def doStore(self):
self.dbModel = QSqlTableModel(self)
self.dbModel.setTable('t1')
ID = self.dbModel.query().lastInsertId() #this returns "None" even though last ID
#in table is 1
thisRecord = QSqlRecord
thisRecord = self.dbModel.record()
thisRecord.setValue(0, ID) # Does not write, even if ID is integer
print ID
thisRecord.setValue(1, int(self.f1.text()))
print int(self.f1.text())
thisRecord.setValue(2, int(self.f2.text()))
print int(self.f2.text())
thisRecord.setValue(3, self.f3.text())
print self.f3.text()
print thisRecord
self.dbModel.insertRecord(ID, thisRecord) # Does not write, even if ID is integer
# Doesn't record field 0 already have ID?
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
sys.exit(app.exec_())
If you want to insert a row at the end of the table it is not necessary to indicate the index, since according to the docs:
PySide.QtSql.QSqlTableModel.insertRecord(row, record)
Parameters:
row - PySide.QtCore.int
record – PySide.QtSql.QSqlRecord
Return type:
PySide.QtCore.bool
Inserts the record after row . If row is negative, the record will be
appended to the end. Calls PySide.QtSql.QSqlTableModel.insertRows()
and PySide.QtSql.QSqlTableModel.setRecord() internally.
Returns true if the row could be inserted, otherwise false.
From the above we can conclude that we should only use as row = -1.
self.dbModel.insertRecord(-1, record)
The best thing is to create the model once and not every time the doStore function is called, we must also use the QSqlRecord of the model with the help of the record () function as it comes loaded with the field names.
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.db = QSqlDatabase.addDatabase('QSQLITE')
self.db.setDatabaseName('example1.sqlite')
self.db.open()
self.db.transaction()
self.db.exec_(
'CREATE TABLE t1'
'(id INTEGER PRIMARY KEY, f1 INTEGER NOT NULL, f2 INTEGER NOT NULL, f3 TEXT NOT NULL)'
)
self.db.commit()
self.db.exec_("INSERT INTO t1 VALUES(1, 10, 20, 'db works fine')")
#Create User Interface
[...]
self.setCentralWidget(widget)
self.dbModel = QSqlTableModel(self)
self.dbModel.setTable('t1')
def doStore(self):
record = self.dbModel.record()
record.setValue(1, int(self.f1.text()))
record.setValue(2, int(self.f2.text()))
record.setValue(3, self.f3.text())
if not self.dbModel.insertRecord(-1, record):
print(self.db.lastError().text())
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.
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)