QgsField won't accept parameter typeName - pyqgis

I'm trying to create new vector layer with the same fields as contained in original layer.
original_layer_fields_list = original_layer.fields().toList()
new_layer = QgsVectorLayer("Point", "new_layer", "memory")
pr = new_layer.dataProvider()
However, when I try:
for fld in original_layer_fields_list:
type_name = fld.typeName()
pr.addAttributes([QgsField(name = fld.name(), typeName = type_name)])
new_layer.updateFields()
QgsProject.instance().addMapLayer(new_layer)
I get a layer with no fields in attribute table.
If I try something like:
for fld in original_layer_fields_list:
if fld.type() == 2:
pr.addAttributes([QgsField(name = fld.name(), type = QVariant.Int)])
new_layer.updateFields()
QgsProject.instance().addMapLayer(new_layer)
... it works like charm.
Anyway ... I'd rather like the first solution to work in case if one wants to automate the process and not check for every field type and then find an appropriate code. Besides - I really am not able to find any documentation about codes for data types. I managed to find this post https://gis.stackexchange.com/questions/353975/get-only-fields-with-datatype-int-in-pyqgis where in comments Kadir pointed on this sourcecode (https://codebrowser.dev/qt5/qtbase/src/corelib/kernel/qvariant.h.html#QVariant::Type).
I'd really be thankful for any kind of direction.

Related

Write scale/nested conversion with ASAMMDF

I am using this snippet in order to create a mf4 file with a value to text table, found in the examples from asammdf's github.
vals = 5
conversion = {
'val_{}'.format(i): i
for i in range(vals)
}
conversion.update(
{
'text_{}'.format(i): 'key_{}'.format(i).encode('ascii')
for i in range(vals)
}
)
sig = Signal(
np.arange(cycles, dtype=np.uint32) % 30,
t,
name='Channel_value_to_text',
conversion=conversion,
comment='Value to text channel',
)
sigs.append(sig)
mdf.append(sigs, comment='arrays', common_timebase=True)
Is there a way to create a table with ##TX blocks and also ##CC blocks?(in order to simulate a scale conversion)
Thank you!
If anyone needs it, I found the answer and it was simpler than expected.
Create another conversion (i.e conversion2) in the same manner as the first one, then reassign the value of one of the references to it.
conversionBlock = from_dict(conversion)
conversionBlock.referenced_blocks["text_4"] = from_dict(conversion2)

How to fix FirstOrDefault returning Null in Linq

My Linq Query keeps returning the null error on FirstOrDefault
The cast to value type 'System.Int32' failed because the materialized value is null
because it can't find any records to match on the ClinicalAssetID form the ClinicalReading Table, fair enough!
But I want the fields in my details page just to appear blank if the table does not have matching entry.
But how can I handle the null issue when using the order by function ?
Current Code:
var ClinicalASSPATINCVM = (from s in db.ClinicalAssets
join cp in db.ClinicalPATs on s.ClinicalAssetID equals cp.ClinicalAssetID into AP
from subASSPAT in AP.DefaultIfEmpty()
join ci in db.ClinicalINSs on s.ClinicalAssetID equals ci.ClinicalAssetID into AI
from subASSINC in AI.DefaultIfEmpty()
join co in db.ClinicalReadings on s.ClinicalAssetID equals co.ClinicalAssetID into AR
let subASSRED = AR.OrderByDescending(subASSRED => subASSRED.MeterReadingDone).FirstOrDefault()
select new ClinicalASSPATINCVM
{
ClinicalAssetID = s.ClinicalAssetID,
AssetTypeName = s.AssetTypeName,
ProductName = s.ProductName,
ModelName = s.ModelName,
SupplierName = s.SupplierName,
ManufacturerName = s.ManufacturerName,
SerialNo = s.SerialNo,
PurchaseDate = s.PurchaseDate,
PoNo = s.PoNo,
Costing = s.Costing,
TeamName = s.TeamName,
StaffName = s.StaffName,
WarrantyEndDate = subASSPAT.WarrantyEndDate,
InspectionDate = subASSPAT.InspectionDate,
InspectionOutcomeResult = subASSPAT.InspectionOutcomeResult,
InspectionDocumnets = subASSPAT.InspectionDocumnets,
LastTypeofInspection = subASSINC.LastTypeofInspection,
NextInspectionDate = subASSINC.NextInspectionDate,
NextInspectionType = subASSINC.NextInspectionType,
MeterReadingDone = subASSRED.MeterReadingDone,
MeterReadingDue = subASSRED.MeterReadingDue,
MeterReading = subASSRED.MeterReading,
MeterUnitsUsed = subASSRED.MeterUnitsUsed,
FilterReplaced = subASSRED.FilterReplaced
}).FirstOrDefault(x => x.ClinicalAssetID == id);
Tried this but doesn't work
.DefaultIfEmpty(new ClinicalASSPATINCVM())
.FirstOrDefault()
Error was:
CS1929 'IOrderedEnumerable<ClinicalReading>' does not contain a definition for 'DefaultIfEmpty' and the best extension method overload 'Queryable.DefaultIfEmpty<ClinicalASSPATINCVM>(IQueryable<ClinicalASSPATINCVM>, ClinicalASSPATINCVM)' requires a receiver of type 'IQueryable<ClinicalASSPATINCVM>'
Feel a little closer with this but still errors
let subASSRED = AR.OrderByDescending(subASSRED => (subASSRED.MeterReadingDone != null) ? subASSRED.MeterReadingDone : String.Empty).FirstOrDefault()
Error:
CS0173 Type of conditional expression cannot be determined because there is no implicit conversion between 'System.DateTime?' and 'string'
The original error means that some of the following properties of the ClinicalASSPATINCVM class - MeterReadingDone, MeterReadingDue, MeterReading, MeterUnitsUsed, or FilterReplaced is of type int.
Remember that subASSRED here
let subASSRED = AR.OrderByDescending(subASSRED => subASSRED.MeterReadingDone).FirstOrDefault()
might be null (no corresponding record).
Now look at this part of the projection:
MeterReadingDone = subASSRED.MeterReadingDone,
MeterReadingDue = subASSRED.MeterReadingDue,
MeterReading = subASSRED.MeterReading,
MeterUnitsUsed = subASSRED.MeterUnitsUsed,
FilterReplaced = subASSRED.FilterReplaced
If that was LINQ to Objects, all these would generate NRE (Null Reference Exception) at runtime. In LINQ to Entities this is converted and executed as SQL. SQL has no issues with expression like subASSRED.SomeProperty because SQL supports NULL naturally even if SomeProperty normally does not allow NULL. So the SQL query executes normally, but now EF must materialize the result into objects, and the C# object property is not nullable, hence the error in question.
To solve it, find the int property(es) and use the following pattern inside query:
SomeIntProperty = (int?)subASSRED.SomeIntProperty ?? 0 // or other meaningful default
or change receiving object property type to int? and leave the original query as is.
Do the same for any non nullable type property, e.g. DateTime, double, decimal, Guid etc.
You're problem is because your DefaultIfEmpty is executed AsQueryable. Perform it AsEnumerable and it will work:
// create the default element only once!
static readonly ClinicalAssPatInVcm defaultElement = new ClinicalAssPatInVcm ();
var result = <my big linq query>
.Where(x => x.ClinicalAssetID == id)
.AsEnumerable()
.DefaultIfEmpty(defaultElement)
.FirstOrDefault();
This won't lead to a performance penalty!
Database management systems are extremely optimized for selecting data. One of the slower parts of a database query is the transport of the selected data to your local process. Hence it is wise to let the DBMS do most of the selecting, and only after you know that you only have the data that you really plan to use, move the data to your local process.
In your case, you need at utmost one element from your DBMS, and if there is nothing, you want to use a default object instead.
AsQueryable will move the selected data to your local process in a smart way, probably per "page" of selected data.
The page size is a good compromise: not too small, so you don't have to ask for the next page too often; not too large, so that you don't transfer much more items than you actually use.
Besides, because of the Where statement you expect at utmost one element anyway. So that a full "page" is fetched is no problem, the page will contain only one element.
After the page is fetched, DefaultIfEmpty checks if the page is empty, and if so, returns a sequence containing the defaultElement. If not, it returns the complete page.
After the DefaultIfEmpty you only take the first element, which is what you want.

nim language ,gintro demo with two columns in a listview / gtktreeview and sortable

For nim language there is only one gui toolkit working for me and that is gintro.
The democode listview compiles and runs nice on my netbsd.
Source:
http://ssalewski.de/gintroreadme.html
But I need a listview(gtktreeview) with two columns, I looked into nim.gtk but can't figure out which "casts" I should spell.
The code in the demo program:
let gtype = typeFromName("gchararray")
let store = newListStore(N_COLUMNS, cast[pointer]( unsafeaddr gtype))
# cast due to bug in gtk.nim
Works nice for N_COLUMNS=1 but not N_COLUMNS:2
Here is the relevant part in nim.gtk:
proc newListStore*(nColumns: int; types: GTypeArray): ListStore =
let gobj = gtk_list_store_newv(int32(nColumns), types)
Second when I have multiple colums I would like to make it sortable by clicking on the header (like an excel table)
I think you need something like this:
let gtypes = [typeFromName("gchararray"), typeFromName("gchararray")] # Be sure to change the types to whatever you need.
let store = newListStore(N_COLUMNS, addr gtype[0]) # You shouldn't need this weird cast here.
Untested but should work. Feel free to join our Gitter/IRC if you need more help :)

Copy range with conditional formatting

I have a range with Conditional Formatting in an existing Excel file. I used EPPlus to copy that range to a new sheet, then I found the conditional formatting was missing.
Is there any way to copy range with conditional formatting using EPPlus?
I found a solution for this. I did not test it on all formattingRuleTypes. (Only needed 2 of them for the moment)
In my application i have 1 template row for each sheet.
var formatList = fromSheet.ConditionalFormatting.ToList();
foreach (var cf in formatList)
{
// sourceRow is the row containing the formatting
if (cf.Address.Start.Row == sourceRow )
{
IExcelConditionalFormattingRule rule = null;
switch (cf.Type)
{
case OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType.GreaterThan:
rule = dest.ConditionalFormatting.AddGreaterThan();
break;
case OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType.GreaterThanOrEqual:
rule = dest.ConditionalFormatting.AddGreaterThanOrEqual();
break;
case OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType.LessThan:
rule = dest.ConditionalFormatting.AddLessThan();
break;
case OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType.LessThanOrEqual:
rule = dest.ConditionalFormatting.AddLessThanOrEqual();
break;
default:
break;
}
rule.Style.Fill = cf.Style.Fill;
rule.Style.Border = cf.Style.Border;
rule.Style.Font = cf.Style.Font;
rule.Style.NumberFormat = cf.Style.NumberFormat;
// I have no clue why the Formula property is not included in the IExcelConditionalFormattingRule interface. So I needed to cast this.
((ExcelConditionalFormattingRule)rule).Formula = ((ExcelConditionalFormattingRule)cf).Formula;
((ExcelConditionalFormattingRule)rule).Formula2 = ((ExcelConditionalFormattingRule)cf).Formula2;
// Calculate the new address for the formatting. This will be different in your case
var adr = new ExcelAddress( dest.Start.Row , cf.Address.Start.Column -1 , dest.Start.Row, cf.Address.Start.Column -1 + cf.Address.Columns -1 );
rule.Address = adr;
I have no clue why the Formula property is not included in the IExcelConditionalFormattingRule interface. So I needed to cast this.
To add to the answer of Luc Wuyts (I can't comment yet due to limited reputation):
// I have no clue why the Formula property is not included in the IExcelConditionalFormattingRule interface. So I needed to cast this.
((ExcelConditionalFormattingRule)rule).Formula = ((ExcelConditionalFormattingRule)cf).Formula;
((ExcelConditionalFormattingRule)rule).Formula2 = ((ExcelConditionalFormattingRule)cf).Formula2;
Some conditional formatting do not have the Formula-options. This cast will work, but applying the Formula properties to conditional formatting options which do not require it will have unforeseen results. Eg. the ConditionalFormatting.AddContainsBlanks() does not require Formula properties, and adding them might mess up the conditional formatting. A better approach is to check the type, and add the formula's only when required.
I had a similar problem, the only way I found to inspect, change or delete a conditional format of a cell or range is looking at the openxml specs. The conditional format is stored in the worksheet, with the range under the attribute sqref. So you can edit that range or add a new.
For example:
DIM p As New ExcelPackage(New FileInfo(ExlReportPath), True)
Dim ws As ExcelWorksheet = p.Workbook.Worksheets(ExlSheetName)
'--Find Node "worksheet" (1 in my case) , Find all Child Nodes "conditionalFormatting" (5 to 11 in my test)
Print.Debug(ws.WorksheetXml.ChildNodes(1).ChildNodes(5).Name)
'--You get: conditionalFormatting
'--Now you can inspect the range:
Print.Debug(ws.WorksheetXml.ChildNodes(1).ChildNodes(5).Attributes("sqref").Value)
'--Will give you the cell address that this formatting applies to example: "D11:D15"
'--you can change delete or add new range if you want, below I add F11:F15
ws.WorksheetXml.ChildNodes(1).ChildNodes(5).Attributes("sqref").Value="D11:D15 F11:F15"
'--You can inspect the rule itself in the InnerXml also...
If you need more details of the markup, google Wouter van Vugt, "Open XML The markup explained". I found it useful and the full document was online (free).
If you find an easier way please post it.
Regards

Zend router: combining a list of urls into a single or list

I have several urls:
/dave
/davina
/dave/chris
/davina/peter
I have entries for all of them in my routes, an example of this is:
routes.dave.route = /dave
routes.dave.defaults.module = default
routes.dave.defaults.controller = person
routes.rcp.defaults.action = index
routes.davina.route = /dave/chris
routes.davina.defaults.module = default
routes.davina.defaults.controller = person
routes.davina.defaults.action = index
I think you can see straight away that I have a duplication issue. Is there a way of combining multiple urls into a single route?
I have played around with using /:person but I also have other content aswell not going to person controller.
routes.person.route = /:personname
routes.person.defaults.module = default
routes.person.defaults.controller = person
routes.person.defaults.action = index
routes.person.reqs.personname = "^[a-zA-Z0-9]+$"
Urls such as /search?person=dave which goes off to the search controller is obviously being killed by the expression above.
Can I express the requirement as a list? "dave|davina|etc" or is there another magical zend method of doing this?
UPDATE:
I was playing around and came across this partial solution:
routes.person.route = /:personname
routes.person.defaults.module = default
routes.person.defaults.controller = person
routes.person.defaults.action = index
routes.person.reqs.personname = "(dave|davina)"
routes.person.route = /:person/:personname
routes.person.defaults.module = default
routes.person.defaults.controller = person
routes.person.defaults.action = index
routes.person.reqs.personname = "(chris|peter)"
I am going to run with this for the moment as is satisfies my requirements but I will leave this open(at least for a day) for anyone to offer there advice. The main issues is that both lists are going to grow, which will eventually cause an issue.
I think I may have the anwser for that.
In ZF, the routes are matched in reverse-order. So the last defined route, is the first matched.
In this case, you should define your /:person/:personname route, then define the others static routes like about page, contact page, etc.
If I understand Zend correctly, it should work. This do that in clear:
url=/about
match route "/about"
url=/david/parker
match route "/:person/:personname"
not "/about" or "/contact"