Sorting by column - sorting

I try to do something that can certainly be done easily but I can find a way.
I have a QtreeView displaying a QFileSystemModel that I custumized with two additionnal columns (sections). I work with directories named by date (YYYYMMDDHHMMSS+num). 'num' at the end of the string is a reference (an integer,ex: 04 or 12 or 53 ....) that can be similar to other directory name. 'num' is displayed in a fourth column.
I would like to group all directories with the similar references (for exemple in an ascending order) and also sort the directories by name (date) within each group.
Can you give me a hand please.
Folders looks like this:
201307
2013072400000053
2013072500000006
2013072600000053
2013072700000006
2013072800000006
2013072900000053
2013073000000006
2013073100000057
201308
2013082400000006
2013082500000053
2013082600000053
2013082700000057
2013082800000006
2013082900000057
2013083000000006
2013083100000053
...
code :
from PyQt4 import QtGui
from PyQt4 import QtCore
import sys
rootpathsource = " "
class MyFileSystemModel(QtGui.QFileSystemModel):
def columnCount(self, parent = QtCore.QModelIndex()):
# Add two additionnal columns for status and Instrument number
return super(MyFileSystemModel, self).columnCount() + 1
def headerData(self, section, orientation, role):
# Set up name for the two additionnal columns
if section == 4 and role == QtCore.Qt.DisplayRole :
return 'Number ref'
else:
return super(MyFileSystemModel, self).headerData(section, orientation, role)
def data(self, index, role):
if index.column() == 4: #if ref
ind = index.parent()
parentString = ind.data().toString()
if parentString in self.fileInfo(index).fileName() and self.fileInfo(index).isDir() == True and role == QtCore.Qt.DisplayRole:
return self.fileInfo(index).fileName()[-2:] # take the last two digits
else:
return super(MyFileSystemModel, self).data(index, role)
if role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignLeft
class TreeViewUi(QtGui.QWidget):
def __init__(self, parent=None):
super(TreeViewUi, self).__init__(parent)
self.model = MyFileSystemModel(self)
self.model.setRootPath(rootpathsource)
self.indexRoot = self.model.index(self.model.rootPath())
self.treeView = QtGui.QTreeView(self)
self.treeView.setExpandsOnDoubleClick(False)
self.treeView.setModel(self.model)
self.treeView.setRootIndex(self.indexRoot)
self.treeView.setColumnWidth(0,300)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.treeView)
class MainGui(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainGui,self).__init__(parent)
#QTreeView widget for files selection
self.view = TreeViewUi()
self.setCentralWidget(self.view)
self.resize(600,700)
def main():
main = MainGui()
main.show()
return main
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
StartApp = main()
sys.exit(app.exec_())

The way to do this is to use the QSortFilterProxyModel class.
All you have to do is create a subclass of it and reimplement its lessThan method. This method has two QModelIndex arguments which can be compared in any way you like before returning either True or False. In your case, you would first check which column was being sorted, and just return the value of the base-class lessThan method for the "standard" columns. But for the "Number Ref" column, you would first compare numerically, and then if that was equal, compare the values of the "Name" column for the same row.
To install the sort-filter proxy, you just need to do:
self.sortFilter = MyCustomSortFilterProxy(self)
self.sortFilter.setSourceModel(self.model)
self.treeView.setModel(self.sortFilter)

Related

SqlAlchemy query filter for Enum class property of Column(Enum(...))

I have a declarative class that has an Enum column, and the Enum has a property that returns True/False based on the specific enumerated name or value. It would simplify life if I could do a query with a filter based on this property, such as the following (see implementation below):
session.query(MyTable).filter(MyTable.letter.is_vowel)
using something like the below straightforward attempt at an expression fails with
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with MyTable.letter has an attribute 'is_vowel'
The below implementation is too simple to allow for construction of the necessary query. Is there a way to do this? I thought maybe something in a Comparator might work, or maybe there's something more sophisticated that would do it?
import enum
from sqlalchemy import (
Column,
Enum,
Integer,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
MyDeclarativeBase = declarative_base()
class Letter(enum.Enum):
A = 1
B = 2
C = 3
D = 4
E = 5
# and so on...
#property
def is_vowel(self):
return self.name in 'AEIOU'
class MyTable(MyDeclarativeBase):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True, autoincrement=True)
letter = Column(Enum(Letter), nullable=False)
#hybrid_property
def is_vowel(self):
"""Return True if the row's letter is a vowel."""
return self.letter.is_vowel
#is_vowel.expression
def is_vowel(cls):
return cls.letter.is_vowel

rest_framework custom order_by

let's see if I can ask the questions correctly?
model.py
class Point(models.Model):
name = models.CharField(
"nome del sistema",
max_length=128,
)
x = models.FloatField(
"cordinata spaziale x",
)
y = models.FloatField(
"cordinata spaziale y",
)
z = models.FloatField(
"cordinata spaziale z",
)
distance = float(0)
def __str__(self) -> str:
return f"{self.name}"
#property
def get_distance(self):
return self.distance
#get_distance.setter
def get_distance(self, point):
"""
ritorna la distanza che ce tra due sistemi
"""
ad = float((point.x-self.x) if point.x > self.x else (self.x-point.x))
bc = float((point.y-self.y) if point.y > self.y else (self.y- point.y))
af = float((point.z-self.z) if point.z > self.z else (self.z-point.z))
return (pow(ad,2)+pow(bc,2)+pow(af,2))**(1/2)
class Meta:
verbose_name = "point"
verbose_name_plural = "points"
in the particular model there are two defs which calculate, save and return the distance with respect to the point we pass them
wenws.py
class PointViewset(viewsets.ModelViewSet):
"""
modelo generico per un systema
"""
queryset = Point.objects.all()
serializer_class = PointSerializers
filterset_class = PointFilter
in the wenws not that much particular to explain and base base the only thing we have to say and that as filters I use 'django_filters'
filters.py
import django_filters as filters
import Point
class CustomOrderFilter(filters.CharFilter):
def filter(self, qs:QuerySet, value):
if value in ([], (), {}, '', None):
return qs
try:
base = qs.get(name=value)
for point in qs:
point.get_distance = base
qs = sorted(qs, key= lambda x: x.get_distance)
except Point.DoesNotExist:
qs = qs.none()
return qs
class PointFilter(filters.rest_framework.FilterSet):
security = filters.ChoiceFilter(choices=security_choices
point= CustomCharFilter(
label = "point"
)
class Meta:
model = Point
fields = {
'name':['exact'],
}
now the complicated thing with 'CustomCharFilter' I pass in the http request the name of the system which then returns to me in the filter as value after I check that it is not empty and I start with returning the point that I have passed with base = qs.get ( name = value)
to then calculate and save the distance for each point with point.get_distance = base '' on the inside of the for, at the end I reorder the QuerySet with qs = sorted (qs, key = lambda x: x.get_distance) '' the problem that both with this way and with another that I have tried the QuerySet it 'transforms' into a list and this does not suit me since I have to return a QuerySet in the order of here I want. I don't know how to do otherwise, since order_by I can't use it since the distance is not inside the database
can someone help me?
So the problem is that you want to filter from a python function which cant be done in a query, as they only speak SQL.
The easy slow solution is to do the filtering in python, this might work if there are just a few Points.
points = list(Point.objects.all())
points.sort(cmp=comparison_function)
The actual real solution is to port that math to Djangos ORM and annotate your queryset with the distance to a given point, this is quite an advanced query, If you tell us your database server you use we can probably help you with that too.
ps. There is an abs() function in python to get absolute value instead of the if/else in get_distance

Rewrite an OpenERP 7 method to Odoo 8 syntax?

I have the following OpenERP 7 method:
# Function to get the vat number (CIF/NIF) and then show it on invoice form view
def _get_vat_num(self, cr, uid, ids, field_name, args=None, context=None):
partner_pool = self.pool.get('res.partner')
invoice_pool = self.pool.get('account.invoice')
res = {}
for inv in self.browse(cr, uid, ids, context=context):
invoice = invoice_pool.browse(cr,uid, inv.id, context=None)
partner = partner_pool.browse(cr, uid, invoice.partner_id.id, context=None)
res[inv.id] = partner.vat
return res
inv_vat = fields.Char(compute='_get_vat_num', string="CIF/NIF")
I need to rewrite it to Odoo v8 syntax. I have tried but it doesn't work:
def _get_vat_num(self):
partner_pool = self.env['res.partner']
invoice_pool = self.env['account.invoice']
res = {}
for inv in self.browse(self.id):
invoice = invoice_pool.browse(inv.id)
partner = partner_pool.browse(invoice.partner_id.id)
res[inv.id] = partner.vat
return res
What should be the correct code?
It looks like you're setting a functional field. You should instead be able to define the field as a related field like so:
inv_vat = fields.Char(string="VAT", related="partner_id.vat")
If you really want it as a functional field, this is how you would do it
inv_vat = fields.Char(string="VAT", compute="_get_vat_num")
def _get_vat_num(self):
# self is a recordset of account.invoice records
for invoice in self:
# to set a functional field, you just assign it
invoice.inv_vat = invoice.partner_id.vat
Check out the recordset documentation: https://www.odoo.com/documentation/8.0/reference/orm.html#recordsets
And the computed fields documentation:
https://www.odoo.com/documentation/8.0/reference/orm.html#computed-fields

How can I create a complete_name field in a custom module for a custom hierarchy like used on product categories in Odoo?

I'm trying to create a field “complete_name” that displays a hierarchy name similar to whats done on the product categories grid but I can't seem to get it to work. It just puts Odoo in an endless loading screen when I access the relevant view using the new field "complete_name".
I have tried to copy the code used in addons/product/product.py and migrate to work with Odoo 9 API by using compute instead of .function type but it did not work.
Can someone help me understand whats wrong? Below is my model class which works fine without the complete_name field in my view.
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )
complete_name = newFields.Char( compute = '_name_get_fnc', string = 'Name' )
def _name_get_fnc( self ):
res = self.name_get( self )
return dict( res )
Your compute function is supposed to define the value of an attribute of your class, not return a value. Ensure the value you are assigning complete_name is a string.
Also name_get() returns a tuple. I am not sure if you really want a string representation of this tuple or just the actual name value.
Try this
def _name_get_fnc( self ):
self.complete_name = self.name_get()[1]
If you really want what is returned by name_get() then try this.
def _name_get_fnc( self ):
self.complete_name = str(self.name_get())
If you are still having issues I would incorporate some logging to get a better idea of what you are setting the value of complete_name to.
import logging
_logger = logging.getLogger(__name__)
def _name_get_fnc( self ):
_logger.info("COMPUTING COMPLETE NAME")
_logger.info("COMPLETE NAME: " + str(self.name_get()))
self.complete_name = self.name_get()
If this does not make it apparent what the issue is you could always try statically assigning it a value in the off chance that there is a problem with your view.
def _name_get_fnc( self ):
self.complete_name = "TEST COMPLETE NAME"
After further review I think I have the answer to my own question. It turns out as with a lot of things its very simple.
Simply use "_inherit" and inherit the product.category
model. This gives access to all the functions and fields
of product.category including the complete_name field
and computes the name from my custom model data. I was
able to remove my _name_get_func and just use the inherited
function.
The final model definition is below. Once this
update was complete I was able to add a "complete_name" field
to my view and the results were as desired!
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_inherit = 'product.category'
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )

pyqt treeview not displaying with overloaded model

I need some help with a treeview. When I specify the model as
class TreeModel(QtGui.QStandardItemModel):
def __init__(self,parent=None):
QtGui.QStandardItemModel.__init__(self,parent)
self.rootItem = QtGui.QStandardItem('root')
I'm able to add items to the tree from the window with
def on_actionAddItem_triggered(self,checked=None):
if checked is None:
return
parent = self.model.invisibleRootItem()
parent.appendRow(QtGui.QStandardItem("test"))
But when I try to overload the model, the treeview doesn't update. The code fails to update the treeview. Can someone explain please?
class TreeModel(QtGui.QStandardItemModel):
def __init__(self,parent=None):
QtGui.QStandardItemModel.__init__(self,parent)
self.rootItem = QtGui.QStandardItem('root')
def data(self,index,role):
if role == QtCore.Qt.DisplayRole:
row = index.row()
text = self.rootItem.child(row)
return text.text()
def columnCount(self,parent=None):
return 1
def rowCount(self,parent=QtCore.QModelIndex()):
return self.rootItem.rowCount()
def headerData(self, column, orientation, role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
if column == 0:
return 'zero'
I discovered that, invisibleRootItem() does not automatically point to the root item. Simply specifying parent = self.model.rootItem resolved the issue.

Resources