PySide/PyQt QAbstractItemModel Questions - pyqt

Ive been researching this all day, and despite looking at things like:
http://www.hardcoded.net/articles/using_qtreeview_with_qabstractitemmodel
http://trevorius.com/scrapbook/uncategorized/pyqt-custom-abstractitemmodel/
http://blog.rburchell.com/2010/02/pyside-tutorial-model-view-programming_22.html
I am entirely stumped. I either can't even get the example working, or when i can, I am unable to bend it to do what I want.
Basically I want to build a model to which i can input a list of dicts. The len() of my list would be my rows, and then len() of any of their keys would be my column count.
I am entirely new to these item models (I usually use TableWidget, ListWidget, etc and their built in implementations), and I'm not understanding how to properly set data, add rows with the beginInsertRows/endInsertRows, and even when i accidnetally get that working, trying to figure out when and how to add my columns as well always causes all sorts of bad.
I was wondering if anyone had any examples I can draw from - something simple with the basis of what I'm missing. for instance:
given
myList = [
{'name': 'John Smith', 'occupation': 'Police', 'Gender': 'Male'},
{'name': 'Jane Smith', 'occupation': 'CEO', 'Gender': 'Female'},
{'name': 'Ranoutta Ideas', 'occupation': 'Not Creativity', 'Gender': 'Male'},
]
I would want to insert that into my model and get 3 rows, each with 3 columns, and maybe the key names as headers.
Is anyone able to point me on a good path?
Thanks in advance!
EDIT
I tried an example from the comments:
import sys
import signal
import PyQt4.QtCore as PCore
import PyQt4.QtGui as PGui
class OneRow(PCore.QObject):
def __init__(self):
self.column0="text in column 0"
self.column1="text in column 1"
class TableModel(PCore.QAbstractTableModel):
def __init__(self):
super(TableModel,self).__init__()
self.myList=[]
def addRow(self,rowObject):
row=len(self.myList)
self.beginInsertRows(PCore.QModelIndex(),row,row)
self.myList.append(rowObject)
self.endInsertRows()
#number of row
def rowCount(self,QModelIndex):
return len(self.myList)
#number of columns
def columnCount(self,QModelIndex):
return 2
#Define what do you print in the cells
def data(self,index,role):
row=index.row()
col=index.column()
if role==PCore.Qt.DisplayRole:
if col==0:
return str( self.myList[row].column0)
if col==1:
return str( self.myList[row].column1)
#Rename the columns
def headerData(self,section,orientation,role):
if role==PCore.Qt.DisplayRole:
if orientation==PCore.Qt.Horizontal:
if section==0:
return str("Column 1")
elif section==1:
return str("Column 2")
if __name__=='__main__':
PGui.QApplication.setStyle("plastique")
app=PGui.QApplication(sys.argv)
#Model
model=TableModel()
model.addRow(OneRow())
model.addRow(OneRow())
#View
win=PGui.QTableView()
win.setModel(model)
#to be able to close wth ctrl+c
signal.signal(signal.SIGINT, signal.SIG_DFL)
#to avoid warning when closing
win.setAttribute(PCore.Qt.WA_DeleteOnClose)
win.show()
sys.exit(app.exec_())
However i end up with a one of errors:
TypeError: invalid result type from TableModel.headerData()
TypeError: invalid result type from TableModel.data()
and no text shows in the cells. I also dont get header labels.
If i change it from returning strings to return QVariant's - i get the text in the cells/headers, but still get tons of those error prints
EDIT:
If i make it fall back to return an empty QVariant, no more error prints - totally missed that. Im going to play with this set up and see if i can get it working as i need, and if so, ill post it here as an answer in case others come accross my struggles

Related

Why my function that creates a pandas dataframe changes the dtype to none when called

I'm working on processing csv files, I was writing my code without functions and it worked, albeit some problems when trying to fillna with a string, before I did a try and except.
For some reason it didn't work before creating the while loop.
My question is why a dataframe object created inside of a function by reading a csv file name I passed when I called the function, returned an empty object? I thought when the dataframe was in memory it wouldn't be destroyed, what am I missing?
My code:
import pandas as pd
grossmargin = 1.2
def read_wholesalefile(name):
mac = name
apple = pd.read_csv(mac)
apple['price'] = apple['Wholesale'] * grossmargin
while True:
try:
apple.fillna('N/A', inplace=True)
break
except ValueError:
print('Not Valid')
read_wholesalefile('Wholesalelist5182021.csv')
Well sorry guys, I figure it out by myself:
I was missing the scope, sorry again for the newb stuff. I just started coding in Python a few months ago(last December) and I'm learning in the process.
What worked for me was to add the scope Global, within the function, seriously I didn't know dataframes behaved as variables ... inside a function.
#My Modified code that works
import pandas as pd
grossmargin = 1.2
def read_wholesalefile(name):
global apple
mac = name
apple = pd.read_csv(mac)
apple['price'] = apple['Wholesale'] * grossmargin
while True:
try:
apple.fillna('N/A', inplace=True)
break
except ValueError:
print('Not Valid')
read_wholesalefile('Wholesalelist5182021.csv')

Trouble working with and updating dictionary using a class and function in Python 3 [Newbie]

I am somewhat new to coding. I have been self teaching myself for the past year or so. I am trying to build a more solid foundation and am trying to create very simple programs. I created a class and am trying to add 'pets' to a dictionary that can hold multiple 'pets'. I have tried changing up the code so many different ways, but nothing is working. Here is what I have so far.
# Created class
class Animal:
# Class Attribute
classes = 'mammal'
breed = 'breed'
# Initializer/Instance Attribrutes
def __init__ (self, species, name, breed):
self.species = species
self.name = name
self.breed = breed
# To get different/multiple user input
#classmethod
def from_input(cls):
return cls(
input('Species: '),
input('Name: '),
input('Breed: ')
)
# Dictionary
pets = {}
# Function to add pet to dictionary
def createpet():
for _ in range(10):
pets.update = Animal.from_input()
if pets.name in pets:
raise ValueError('duplicate ID')
# Calling the function
createpet()
I have tried to change it to a list and use the 'append' tool and that didn't work. I am sure there is a lot wrong with this code, but I am not even sure what to do anymore. I have looked into the 'collections' module, but couldn't understand it well enough to know if that would help or not. What I am looking for is where I can run the 'createpet()' function and each time add in a new pet with the species, name, and breed. I have looked into the sqlite3 and wonder if that might be a better option. If it would be, where would I go to learn and better understand the module (aka good beginner tutorials). Any help would be appreciated. Thanks!
(First of all, you have to check for a duplicate before you add it to the dictionary.)
The way you add items to a dictionary x is
x[y] = z
This sets the value with the key y equal to z or, if there is no key with that name, creates a new key y with the value z.
Updated code:
(I defined this as a classmethod because the from_input method is one as well and from what I understand of this, this will keep things working when it comes to inheriting classes, for further information you might want to look at this)
#classmethod
def createpet(cls):
pet = cls.from_input()
if pet.name in cls.pets:
raise ValueError("duplicate ID")
else:
cls.pets[pet.name] = pet

Creating a list of Class objects from a file with no duplicates in attributes of the objects

I am currently taking some computer science courses in school and have come to a dead end and need a little help. Like the title says, I need of create a list of Class objects from a file with objects that have a duplicate not added to the list, I was able to successfully do this with a python set() but apparently that isn't allowed for this particular assignment, I have tried various other ways but can't seem to get it working without using a set. I believe the point of this assignment is comparing data structures in python and using the slowest method possible as it also has to be timed. my code using the set() will be provided.
import time
class Students:
def __init__(self, LName, FName, ssn, email, age):
self.LName = LName
self.FName = FName
self.ssn = ssn
self.email = email
self.age = age
def getssn(self):
return self.ssn
def main():
t1 = time.time()
f = open('InsertNames.txt', 'r')
studentlist = []
seen = set()
for line in f:
parsed = line.split(' ')
parsed = [i.strip() for i in parsed]
if parsed[2] not in seen:
studentlist.append(Students(parsed[0], parsed[1], parsed[2], parsed[3], parsed[4]))
seen.add(parsed[2])
else:
print(parsed[2], 'already in list, not added')
f.close()
print('final list length: ', len(studentlist))
t2 = time.time()
print('time = ', t2-t1)
main()
A note, that the only duplicates to be checked for are those of the .ssn attribute and the duplicate should not be added to the list. Is there a way to check what is already in the list by that specific attribute before adding it?
edit: Forgot to mention only 1 list allowed in memory.
You can write
if not any(s.ssn==parsed[2] for s in studentlist):
without committing to this comparison as the meaning of ==. At this level of work, you probably are expected to write out the loop and set a flag yourself rather than use a generator expression.
Since you already took the time to write a class representing a student and since ssn is a unique identifier for the instances, consider writing an __eq__ method for that class.
def __eq__(self, other):
return self.ssn == other.ssn
This will make your life easier when you want to compare two students, and in your case make a list (specifically not a set) of students.
Then your code would look something like:
with open('InsertNames.txt') as f:
for line in f:
student = Student(*line.strip().split())
if student not in student_list:
student_list.append(student)
Explanation
Opening a file with with statement makes your code more clean and
gives it the ability to handle errors and do cleanups correctly. And
since 'r' is a default for open it doesn't need to be there.
You should strip the line before splitting it just to handle some
edge cases but this is not obligatory.
split's default argument is ' ' so again it isn't necessary.
Just to clarify the meaning of this item is that the absence of a parameter make the split use whitespaces. It does not mean that a single space character is the default.
Creating the student before adding it to the list sounds like too
much overhead for this simple use but since there is only one
__init__ method called it is not that bad. The plus side of this
is that it makes the code more readable with the not in statement.
The in statement (and also not in of course) checks if the
object is in that list with the __eq__ method of that object.
Since you implemented that method it can check the in statement
for your Student class instances.
Only if the student doesn't exist in the list, it will be added.
One final thing, there is no creation of a list here other than the return value of split and the student_list you created.

Tkinter, curselection()[0], IndexError: tuple index out of range

I am connecting frontend to backend of a database application using Tkinter and sqlite3.
Need help finding potential reasons that resulted in this error:
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Peng\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1699, in __call__
    return self.func(*args)
  File "frontend.py", line 9, in get_selected_row
    index=list1.curselection()[0]
IndexError: tuple index out of range
But list1.curselection()[0] is just the id, why is it out of range?
Any help on finding where went wrong will be greatly appreciated!!!
My code:
frontend.py:
from tkinter import *
import backend
window=Tk()
list1.bind('<<ListboxSelect>>',get_selected_row)
def update_command():
backend.update(selected_tuple[0],title_text.get(),author_text.get(),year_text.get(),isbn_text.get())
def get_selected_row(event):
global selected_tuple
index=list1.curselection()[0]
selected_tuple=list1.get(index)
e1.delete(0,END)
e1.insert(END,selected_tuple[1])
e2.delete(0,END)
e2.insert(END,selected_tuple[2])
e3.delete(0,END)
e3.insert(END,selected_tuple[3])
e4.delete(0,END)
e4.insert(END,selected_tuple[4])
backend.py:
import sqlite3
def update(id,title,author,year,isbn):
conn=sqlite3.connect("books.db")
cur=conn.cursor()
cur.execute("UPDATE book SET title=?,author=?,year=?,isbn=? WHERE id=?",(title,author,year,isbn,id))
conn.commit()
conn.close()
Your method is being triggered when there is nothing selected. The easiest fix is to simply check if the tuple is empty:
def get_selected_row(event):
global selected_tuple
index=list1.curselection()
if index: # if the tuple is not empty
selected_tuple=list1.get(index[0])
e1.delete(0,END)
e1.insert(END,selected_tuple[1])
e2.delete(0,END)
e2.insert(END,selected_tuple[2])
e3.delete(0,END)
e3.insert(END,selected_tuple[3])
e4.delete(0,END)
e4.insert(END,selected_tuple[4])
A more proper fix is to find out when it's being triggered like that and prevent it.
it happens when you click into the list1 while it is empty
this is the line that triggers it
list1.bind('<<ListboxSelect>>',get_selected_row)
the solution mentioned above is suitable since you don't seem to have any other code manipulating this event
however, i would word the solution a bit differently, to be consistent with your code naming conventions:
def get_selected_row(event):
global selected_tuple
if list1.curselection():
index=list1.curselection()[0]
selected_tuple=list1.get(index)
e1.delete(0,END)
e1.insert(END,selected_tuple[1])
e2.delete(0,END)
e2.insert(END,selected_tuple[2])
e3.delete(0,END)
e3.insert(END,selected_tuple[3])
e4.delete(0,END)
e4.insert(END,selected_tuple[4])
Since the listbox is empty, then list1.curselection()will be an empty list with no items. Trying to access the first item of that list with [0] in line 3 will throw an error since there is no first item in the list.
def get_selected_row(event):
try:
global selected_tuple
index=list1.curselection()
selected_tuple=list1.get(index[0])
e1.delete(0,END)
e1.insert(END,selected_tuple[1])
e2.delete(0,END)
e2.insert(END,selected_tuple[2])
e3.delete(0,END)
e3.insert(END,selected_tuple[3])
e4.delete(0,END)
e4.insert(END,selected_tuple[4])
except IndexError:
pass
When the get_selected_row function is called, Python will try to execute the indentedblock under try. If there is an IndexErrornone of the lines under try will be executed. Instead the line under except will be executed which is pass. Thepass stetementmeans do nothing. So the function will do nothing when there's an empty listbox.

Getting index of kivy spinner selection

Usually the spinner widget returns the selected text. In my app i plan to use some non standard letters (like: ü,ö,è) as values to populate the spinner.
The below example works on my windows machine but I hope to avoid handling any Non-ASCII characters to prevent problems on other OS's.
Is there a way to access the index of the spinner selection directly without using the list.index(text) method?
# This Python file uses the following encoding: utf-8
from kivy.base import runTouchApp
from kivy.uix.spinner import Spinner
spinnervalues = ['one_ö','two_ü','three_ä']
spinner = Spinner(
# default value shown
text='Home',
# available values
values=spinnervalues,
# just for positioning in our example
size_hint=(None, None),
size=(100, 44),
pos_hint={'center_x': .5, 'center_y': .5})
def show_selected_value(spinner, text):
print('The spinner', spinner, 'have text', text)
list_index = spinnervalues.index(text)
print('This list item index:', list_index)
spinner.bind(text=show_selected_value)
runTouchApp(spinner)
I have tried something like:
spinner.bind(index=show_selected_value)
but without success.
I know this original question is very old now, but for future reference you could try:
list_index = spinner.values.index(text)
print('This list item index:', list_index)
which prints:
'This list item index:', 2
assuming you picked the 3rd item in the list of spinner.values (zero based index).
I use a similar construct in my code because the text (spinnervalues) may change based on the language in use.
And this ties the value back to current spinner object, incase you're using multiple spinners in your application.
hope that helps someone.

Resources