Implementation of clone in QStandardItem subclass - python

I am using a QStandardItemModel with a QTreeView to display custom Items. The items come in three different types FILTER_TYPE, MODIFIER_TYPE and GROUP_TYPE.
I would like to be able to reorder items within the model using drag and drop in the view (InternalMove). If I understood it correctly, I have to use setItemPrototype(MyItem()) on my model in order for it to use the custom MyItem and not the general QStandardItem when moving items.
My understanding was that a new instance of the custom MyItem gets created and then all data and flags from the old item are copied over to the new item. However, it seems the model only initialises a new MyItem and never copies the data.
Therefore: How do I reimplement QStandardItem.clone() in the MyItem subclass to copy all data and flags into the new item? Do I have to manually go through all the custom data roles and assign their value to the new item?
The Item class looks like this:
class MyItem(QtGui.QStandardItem):
FILTER_TYPE = QtGui.QStandardItem.UserType + 1
MODIFIER_TYPE = QtGui.QStandardItem.UserType + 2
GROUP_TYPE = QtGui.QStandardItem.UserType + 3
TYPE = QtCore.Qt.UserRole + 0
NAME = QtCore.Qt.UserRole + 1
IS_PROCESSED = QtCore.Qt.UserRole + 5
OUTPUT = QtCore.Qt.UserRole + 6
FN = QtCore.Qt.UserRole + 7
DEFAULT_PARAMS = QtCore.Qt.UserRole + 8
PARAMETER_SET = QtCore.Qt.UserRole + 9
def __init__(self):
super().__init__()
self.name = ""
self.full_name = ""
self.description = ""
self.fn = None
self.default_params = None
self.parameter_set = None
self.is_active = True
self.is_processed = False
self.output = None
self.icon = QtGui.QIcon()
def clone(self):
item = Item()
??? WHAT GOES HERE TO COPY ALL DATA AND FLAGS ???
return item
def __setattr__(self, name, value):
if name == 'name':
self.setData(value, self.NAME)
elif name == 'full_name':
self.setData(value, QtCore.Qt.DisplayRole)
self.setData(value, QtCore.Qt.EditRole)
elif name == 'description':
self.setData(value, QtCore.Qt.ToolTipRole)
...
else:
super().__setattr__(name, value)
def __getattribute__(self, name):
if name == 'name':
return self.data(self.NAME)
elif name == 'full_name':
return self.data(QtCore.Qt.DisplayRole)
elif name == 'description':
return self.data(QtCore.Qt.ToolTipRole)
...
else:
return super().__getattribute__(name)
def initializeItem(self, type_, name, full_name, description="", fn=None, default_params=None):
self.name = name
self.full_name = full_name
self.description = description
self.fn = fn
self.default_params = default_params
self.parameter_set = ParameterSet(params_list=default_params)
self.setData(type_, self.TYPE)
flags = QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsDragEnabled|QtCore.Qt.ItemIsUserCheckable|QtCore.Qt.ItemIsEnabled
if type_ == self.FILTER_TYPE:
self.icon = QtGui.QIcon('resources/filter.png')
flags = flags|QtCore.Qt.ItemNeverHasChildren
elif type_ == self.MODIFIER_TYPE:
self.icon = QtGui.QIcon('resources/modifier.png')
flags = flags|QtCore.Qt.ItemIsDropEnabled
elif type_ == self.GROUP_TYPE:
self.icon = QtGui.QIcon('resources/folder.png')
flags = flags|QtCore.Qt.ItemIsDropEnabled|QtCore.Qt.ItemIsEditable
self.setFlags(flags)
def type(self):
return self.data(self.TYPE)
The Model implementation looks like this:
from tree.items import MyItem
class TreeModel(QtGui.QStandardItemModel):
def __init__(self):
super().__init__()
self.setItemPrototype(MyItem())

The logic of "clone" is to create an object with the same information of the item, so in this case you are using the roles to store that information so you must copy all that information in the new item, in this case you can use QDataStream:
def clone(self):
item = MyItem()
ba = QtCore.QByteArray()
ds = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
ds << self
ds = QtCore.QDataStream(ba)
ds >> item
return item

Related

Proxy Class in Python 3

I wrote a simple Proxy class in python3, but I have a problem with "was_called" function
class Proxy:
last_invoked = ""
calls = {}
def __init__(self, obj):
self._obj = obj
def __getattr__(self, item):
attrs = dir(self._obj)
if item in attrs:
Proxy.last_invoked = item
if item in Proxy.calls.keys():
Proxy.calls[item] += 1
else:
Proxy.calls[item] = 1
if item in Proxy.calls.keys():
Proxy.calls[item] += 1
else:
Proxy.calls[item] = 1
return getattr(self._obj, item)
else:
raise Exception('No Such Method')
def last_invoked_method(self):
if Proxy.last_invoked == "":
raise Exception('No Method Is Invoked')
else:
return Proxy.last_invoked
def count_of_calls(self, method_name):
if method_name in Proxy.calls.keys():
return Proxy.calls[method_name]
return 0
def was_called(self, method_name):
if method_name in Proxy.calls.keys():
if Proxy.calls[method_name] > 0: return True
return False
class Radio():
def __init__(self):
self._channel = None
self.is_on = False
self.volume = 0
def get_channel(self):
return self._channel
def set_channel(self, value):
self._channel = value
def power(self):
self.is_on = not self.is_on
radio = Radio()
radio_proxy = Proxy(radio)
radio.number = 3
radio_proxy.number = 3
radio_proxy.power()
print(radio_proxy.was_called("number"))
print(radio_proxy.was_called("power"))
"was_called" function is work for functions and attributes that is in radio at first such as "power", but it's not work for new attributes that we add such as "number".
I expect for both print "True", because both of "power" and "number" is called. but first print return False!
What do you suggest?
def Proxy(class_type):
class ProxyClass(class_type):
def __init__(self, *args, **kwargs):
# Set your _calls and _last_invoked here, so that they are not class attributes (and are instead instance attributes).
self._calls = {}
self._last_invoked = ""
# Pass the arguments back to the class_type (in our case Radio) to initialize the class.
super().__init__(*args, **kwargs)
def __getattribute__(self, item):
# We must do this prelimary check before continuing on to the elif statement.
# This is since _calls and _last_invoked is grabbed when self._last_invoked/self._calls is called below.
if item in ("_calls", "_last_invoked"):
return super(ProxyClass, self).__getattribute__(item)
elif not item.startswith("_"):
self._last_invoked = item
self._calls[item] = 1 if item not in self._calls.keys() else self._calls[item] + 1
return super(ProxyClass, self).__getattribute__(item)
def __setattr__(self, item, val):
# Wait until _calls is initialized before trying to set anything.
# Only set items that do not start with _
if not item == "_calls" and not item.startswith("_"):
self._calls[item] = 0
super(ProxyClass, self).__setattr__(item, val)
def last_invoked_method(self):
if self._last_invoked == "":
raise Exception('No Method Is Invoked')
else:
return self._last_invoked
def count_of_calls(self, method_name):
return self._calls[method_name] if method_name in self._calls.keys() else 0
def was_called(self, method_name):
return True if method_name in self._calls.keys() and self._calls[method_name] > 0 else False
return ProxyClass
#Proxy
class Radio():
def __init__(self):
self._channel = None
self.is_on = False
self.volume = 0
def get_channel(self):
return self._channel
def set_channel(self, value):
self._channel = value
def power(self):
self.is_on = not self.is_on
radio = Proxy(Radio)()
radio.number = 3 # Notice that we are only setting the digit here.
radio.power()
print(radio._calls)
print(radio.number) # Notice that this when we are actually calling it.
print(radio._calls)
outputs:
{'is_on': 0, 'volume': 0, 'number': 0, 'power': 1}
3
{'is_on': 0, 'volume': 0, 'number': 1, 'power': 1}
A few modifications here and there, but you should be able to see the bigger idea by reading through the code. From here you should be able to modify the code to your liking. Also note that any variable that starts with _ is automatically removed from the _calls dictionary.
If you rather not use the decorator #Proxy, you may initialize your Radio class (as a proxy) like so:
# Second parentheses is where your Radio args go in.
# Since Radio does not take any args, we leave it empty.
radio_proxy = Proxy(Radio)()
Also, make sure to understand the difference between class attributes, and instance attributes.
Edit:
class Test:
def __init__(self, var):
self.var = var
self.dictionary = {}
def __getattribute__(self, item):
print("we are GETTING the following item:", item)
# If we don't do this, you end up in an infinite loop in which Python is
# trying to get the `dictionary` class to do `self.dictionary['dictionary'] = ...`
if item == "dictionary":
super(Test, self).__getattribute__(item)
else:
self.dictionary[item] = "Now we can use this!"
return super(Test, self).__getattribute__(item)
def __setattr__(self, item, key):
print("we are SETTING the following item:", item)
super(Test, self).__setattr__(item, key)
Notice:
test = Test(4)
outputs:
we are SETTING the following item: var
we are SETTING the following item: dictionary
then following it:
test.var
outputs:
we are GETTING the following item: var
we are GETTING the following item: dictionary

how to iterate list of objects

I have a list of objects. I would like to check some string if that string exists as a field value any object in the list. for example,
class Ani:
name = ''
def __init__(self, name):
self.name = name
def getName(self):
return self.name
animal1 = Ani('alica')
animal2 = Ani('rex')
animal3 = Ani('bobik')
animal4 = Ani('dobik')
animal5 = Ani('sobik')
a = [animal1, animal2, animal3,animal4,animal5]
my problem to write a code in order to see if there is an object with given name. for example "chip".
You can iterate over the array of objects, and check with each object's getName function.
class Ani:
name = ''
def __init__(self, name):
self.name = name
def getName(self):
return self.name
animal1 = Ani('alica')
animal2 = Ani('rex')
animal3 = Ani('bobik')
animal4 = Ani('dobik')
animal5 = Ani('sobik')
animals = [animal1, animal2, animal3,animal4,animal5]
searched_animal = 'rex'
for animal in animals:
if animal.getName() == searched_animal:
print('Found')
break
You can use any plus a comprehension:
any(animal.getName() == "chip" for animal in animals)
Iterating a list containing anything is quite simple really. Like so:
animal_to_find = "someAnimal"
for animal in animals:
if animal.getName() == animal_to_find:
print("Found a match for: " + animal)
You can use the getName method present in Ani class for this program
class Ani:
name = ''
def __init__(self, name):
self.name = name
def getName(self):
return self.name
animal1 = Ani('alica')
animal2 = Ani('rex')
animal3 = Ani('bobik')
animal4 = Ani('dobik')
animal5 = Ani('sobik')
animals = [animal1, animal2, animal3,animal4,animal5]
key = 'chip'
flag=0
for animal in animals:
if animal.getName() == key:
print('Found')
flag=1
break
if flag==0:
print("Not Found")

Python, remove object from List

I am still learning Python and I have a problem. If my question isn't that clear, please be nice!
Is it possible that while using a list, I can delete an object from the list if only one object matches
So for example:
driver.addDriver(Driver("Ben", "BBB"))
driver.removeDriver("Ben", "123")
Can I remove the driver name and print as None while still showing the vehicle number. Thanks.
class Driver:
def __init__(self, name, vehNo):
self._name = name
self._vehNo = vehNo
#property
def name(self):
return self._name
#property
def vehNo(self):
return self._vehNo
#vehNo.setter
def vehNo(self, newVehNo):
self._vehNo = newVehNo
def __str__(self):
return 'Driver Name: {} Vehicle Number: {}'.format(self._name, self._vehNo)
class TransportServices:
def __init__(self):
self._drivers = []
def searchDriver(self, name = None, vehNo = None):
for d in self._drivers:
if d.name == name and d.vehNo == vehNo:
return d
return None
#############################################################################
def addDriver(self, driver):
d = self.searchDriver(driver.name)
if d is None:
self._drivers.append(driver)
return True
else:
return False
#############################################################################
def removeDriver(self, name = None, vehNo = None):
d = self.searchDriver(name, vehNo)
if d is None:
return False
else:
self._drivers.remove(d)
#############################################################################
def __str__(self):
drivers = [str(d) for d in self._drivers]
return "{} ".format('\n'.join(drivers))
def main():
driver = TransportServices()
driver.addDriver(Driver("Alan", "AAA"))
driver.addDriver(Driver("Ben", "BBB"))
driver.removeDriver("Ben", "123")
print(driver)
main()
Basically what you are looking for is not deleting the object but updating it.
You can update the corresponding object as below:
for driver in self.drivers:
if driver.name == 'Bob': # or driver vehNo == 'BBB'
driver.name = None
Also for your case,
you could rather use a dictionary which is the same
as a hash map in Java.
You can do some thing like below:
self.drivers = {}
self.driver['vehicle Num'] = theDriverObject
so that when you need to access or update you can do it instantly i.e. O(1) without having to loop through all the drivers.

I cannot understand a case of passing an object as a parameter

I have a class Node with a function defined
class Node(object):
def __init__(self, index, state = None, input = None, directed_neighbours=False):
"""
Parameters
----------
index : int
Node index. Must be unique in the graph.
"""
self._input = input
self.state = state
#self._status = 'active'
self._index = int(index)
self._neighbours = set()
self._port_count = 0
self._ports = []
if directed_neighbours:
self._predecessors = set()
self._successors = self._neighbours
self._directed_neighbours = True
else:
self._successors = self._neighbours
self._predecessors = self._neighbours
self._directed_neighbours = False
#property
def setStatus(self, status):
self._status = status
I have another function
def init(node):
node.setStatus('active')
Now, I have a class
class DistAlgo:
def __init__(self, name, initFunc, states, messages, sendFunc, receiveFunc, stoppingCheck):
self.name = name
#self.inputGraph = inputGraph
self.initFunc = initFunc
self.states = states
self.messages = messages
self.sendFunc = sendFunc
self.receiveFunc = receiveFunc
self.comm_round = 0
self.stoppingCheck = stoppingCheck
def run(self, inputGraph):
for node in inputGraph.nodes:
print('hello', node)
node.state = self.initFunc(node)
<....more code...>
When I create an object of DistAlgo
myalgo = DistAlgo('BMM', init, states, messages, send, receive, stoppingCheck)
and then call its run function:
myalgo.run(problemGraph)
I get an error in the init function above, as:
TypeError: setStatus() missing 1 required positional argument: 'status'
I surely am doing more than one thing wrong I guess, as this is my first Python try. Please point them out!
Properties work a bit differently:
#property
def status(self):
return self._status
#status.setter
def status(self, status):
self._status = status
Now you can set the value with an assignment:
node.status = 'active'

Unit testing: More Actions than Expected after calling addAction Method

Here is my class:
class ManagementReview(object):
"""Class describing ManagementReview Object.
"""
# Class attributes
id = 0
Title = 'New Management Review Object'
fiscal_year = ''
region = ''
review_date = ''
date_completed = ''
prepared_by = ''
__goals = [] # List of <ManagementReviewGoals>.
__objectives = [] # List of <ManagementReviewObjetives>.
__actions = [] # List of <ManagementReviewActions>.
__deliverables = [] # List of <ManagementReviewDeliverable>.
__issues = [] # List of <ManagementReviewIssue>.
__created = ''
__created_by = ''
__modified = ''
__modified_by = ''
def __init__(self,Title='',id=0,fiscal_year='',region='',review_date='',
date_completed='',prepared_by='',created='',created_by='',
modified='',modified_by=''):
"""Instantiate object.
"""
if id:
self.setId(id)
if Title:
self.setTitle(Title)
if fiscal_year:
self.setFiscal_year(fiscal_year)
if region:
self.setRegion(region)
if review_date:
self.setReview_date(review_date)
if date_completed:
# XXX TODO: validation to see if date_completed pattern matches ISO-8601
self.setDate_completed(date_completed)
if prepared_by:
self.setPrepared_by(prepared_by)
if created:
# XXX TODO: validation to see if date_completed pattern matches ISO-8601
self.setCreated(created)
else:
self.setCreated(self.getNow())
if created_by:
self.setCreated_by(created_by)
self.__modified = self.getNow()
if modified_by:
self.__modified_by = modified_by
def __str__(self):
return "<ManagementReview '%s (%s)'>" % (self.Title,self.id)
def __setattr__(self, name, value): # Override built-in setter
# set the value like usual and then update the modified attribute too
object.__setattr__(self, name, value) # Built-in
self.__dict__['__modified'] = datetime.now().isoformat()
def getActions(self):
return self.__actions
def addAction(self,mra):
self.__actions.append(mra)
def removeAction(self,id):
pass # XXX TODO
I have this test:
from datetime import datetime
import random
import unittest
from ManagementReview import ManagementReview, ManagementReviewAction
# Default Values for ManagementReviewAction Object Type
DUMMY_ID = 1
DUMMY_ACTION = 'Action 1'
DUMMY_OWNER = 'Owner 1'
DUMMY_TITLE = 'Test MR'
DUMMY_FISCAL_YEAR = '2011'
DUMMY_REGION = 'WO'
DUMMY_REVIEW_DATE = '2009-01-18T10:50:21.766169',
DUMMY_DATE_COMPLETED = '2008-07-18T10:50:21.766169'
DUMMY_PREPARED_BY = 'test user'
DUMMY_CREATED = '2002-07-18T10:50:21.766169'
DUMMY_CREATED_BY = 'test user 2'
DUMMY_MODIFIED = datetime.now().isoformat()
DUMMY_MODIFIED_BY = 'test user 3'
class TestManagementReviewSetAction(unittest.TestCase):
def setUp(self):
self.mr = ManagementReview(DUMMY_TITLE,DUMMY_ID,fiscal_year=DUMMY_FISCAL_YEAR,region=DUMMY_REGION,
review_date=DUMMY_REVIEW_DATE,date_completed=DUMMY_DATE_COMPLETED,
prepared_by=DUMMY_PREPARED_BY,created=DUMMY_CREATED,
created_by=DUMMY_CREATED_BY,modified=DUMMY_MODIFIED,
modified_by=DUMMY_MODIFIED_BY)
def tearDown(self):
self.mr = None
def test_add_action(self):
for i in range(1,11):
mra = ManagementReviewAction(i,'action '+str(i),'owner '+str(i))
self.mr.addAction(mra)
self.assertEqual(len(self.mr.getActions()),10)
def test_remove_action(self):
print len(self.mr.getActions())
for i in range(1,11):
mra = ManagementReviewAction(i,'action '+str(i),'owner '+str(i))
self.mr.addAction(mra)
self.mr.removeAction(3)
self.assertEqual(len(self.mr.getActions()),9)
if __name__ == '__main__':
unittest.main()
The first test passes. That is, self.mr.getActions() has 10 actions.
However, when I run the 2nd test, test_remove_action, the value for len(self.mr.getActions()) is 10. At this point, though, it should be 0.
Why is this?
Thanks
see if you are keeping track of actions in a class attribute of ManagementReview as opposed to an instance attribute
A class attribute will be something like
class Spam(object):
actions = []
and an instance attribute will look something like
class Spam(object):
def __init__(self):
self.actions = []
What happens is that actions = [] is executed when the class is created and all instances of the class share the same list.
EDIT:
In light of your update, I can see that this is definitely what is going on

Categories

Resources