I have created a TableView in QML, connected to a SoftFilterProxyModel. The data displays fine, and when I click on a row my "selectRow" function runs, and receives the correct row number. However, nothing shows as selected. I based my design on this SO question
The relevant code is:
ItemSelectionModel {
id: companyTableISM
model: companySFPM
}
function selectRow(row) {
console.log("In selectRow row "+row);
companyTableISM.select(companySFPM.index(row, 0), ItemSelectionModel.select | ItemSelectionModel.current );
console.log(companyTableISM.selectedIndexes);
console.log(companyTableISM.hasSelection);
}
So when I click a row it outputs:
qml: In selectRow row 3
qml: []
qml: false
Since my selectRow function is receiving the correct row number, the model (companySFPM) matches the one used by the TableView, why do my 2 log statements showing nothing selected and false (hasSelection)?
I believe you have two typos in the line:
ItemSelectionModel.select | ItemSelectionModel.current
Notice the capitalization of the enumerated types? It should be:
ItemSelectionModel.Select | ItemSelectionModel.Current
Current and Select are selection flags enum of select(). For more selection flags you can read this .
Replace s and c of select and current with capital letter.
companyTableISM.select(companySFPM.index(row, 0), ItemSelectionModel.Select | ItemSelectionModel.Current);
Related
Three example rows from that column are shown below.
{'gender': 'Female', 'document_type': 'driving_licence', 'date_of_expiry': '2024-03-03', 'issuing_country': 'GBR'}
{'nationality': 'DEU', 'document_type': 'national_identity_card', 'date_of_expiry': '2020-11-19', 'issuing_country': 'DEU'}
{'gender': 'Female', 'nationality': 'FRA', 'document_type': 'passport', 'date_of_expiry': '2024-01-22', 'issuing_country': 'FRA'}
My desired outcome would be:
gender | document_type | document_type | date_of_expiry | issuing country | nationality | national_identity_card |
e.g.
Female | driving_licence | 2024-03-03 | GBR | NULL | NULL
NULL | national_identity_card | 2020-11-19 | DEU | DEU
.
.
.
Any help would be great :)
Indeed it looks like JSON. You could replace all the ' with " and paste the records here: https://codebeautify.org/jsonviewer
The complete string for the examples you provided would be:
{
"example": [
{
"gender": "Female",
"document_type": "driving_licence",
"date_of_expiry": "2024-03-03",
"issuing_country": "GBR"
},
{
"nationality": "DEU",
"document_type": "national_identity_card",
"date_of_expiry": "2020-11-19",
"issuing_country": "DEU"
},
{
"gender": "Female",
"nationality": "FRA",
"document_type": "passport",
"date_of_expiry": "2024-01-22",
"issuing_country": "FRA"
}
]
}
After that, clicking the button JSON to CSV generates the values sorted by columns like this:
gender,document_type,date_of_expiry,issuing_country
Female,driving_licence,2024-03-03,GBR
,national_identity_card,2020-11-19,DEU
Female,passport,2024-01-22,FRA
While the answer from #jota seems to have solved your problem, I have an alternative solution for that works mostly automatically, though it depends if you always receive the data as objects, ideally in an array.
A potential Apps Script solution
First you need to create a Google Sheet and fill in the headers. The text must match exactly the title of the property titles you are receiving. For example, document_type not Document Type.
Then open the Script Editor by clicking on the 'Tools' menu, then select 'Script editor...'.
Replace the code with the following:
function addToSheet() {
// Ideally this data gets created automatically but here is the example you gave.
let example = [
{
gender: "Female",
document_type: "driving_licence",
date_of_expiry: "2024-03-03",
issuing_country: "GBR",
},
{
nationality: "DEU",
document_type: "national_identity_card",
date_of_expiry: "2020-11-19",
issuing_country: "DEU",
},
{
gender: "Female",
nationality: "FRA",
document_type: "passport",
date_of_expiry: "2024-01-22",
issuing_country: "FRA",
},
];
// Defining where to put the data
let ss = SpreadsheetApp.getActiveSpreadsheet(); // Spreadsheet the script is attached to
let sheet = ss.getSheetByName("Sheet1"); // Put the name of your sheet here
let sheetData = sheet.getDataRange().getValues(); // Gets all data from sheet.
// Take headers from data
let headers = sheetData.shift(); // removes first row, which is the headers, of data and assigns to variable
// For each entry, create an array, and fill it according to the header structure.
example.forEach((entry) => {
let newRow = []; // initializing new array
// build the new row according to the header structure.
headers.forEach(header => {
newRow.push(entry[header]); // add cell value to row, if not present in entry, will fill with undefined
})
sheet.appendRow(newRow); // add built row to sheet
});
}
I adapted the structure provided by #jota. What would be up to you is to adapt the data you are getting to be an array of objects. What I have defined as example, would be the necessary format.
Then if you run the code, it will append the rows in the right place.
References and explanation
SpreadsheetApp - the service within Apps Script that allows you to manipulate spreadsheets.
Spreadsheet - the spreadsheet object.
Sheet - the sheet object, in the example above, the sheet must be named "Sheet1" - though you can change that in your code to adapt to your needs, of course.
Range - The methods associated with ranges (selections etc).
Append Row - The method used to add the row at the end.
getDataRange - The method used to select the range containing all the data in the particular sheet. In this case, it is used to dynamically get the headers.
getValues - Used in conjunction with getDataRange to actually return the values. getDataRange only returns the range object which contains the values, but has a lot more to it too. With getValues you get a simple two dimensional array.
The script gets the headers in an array, it uses the headers array to build a row for the sheet for each object defined in example, and appends each row to the sheet after its built.
This can be adapted to many situations, but the advantage it has, is that the object can have all or none of the fields, in any order, and it will update the spreadsheet correctly regardless. As mentioned previously, the only issue you may encounter, is getting the initial data into the right format.
I've got a system that generates and automatically maintains lots of spreadsheets on a Drive account.
Whenever I add data to the sheet I run a 'format' method to pass over and make sure everything is ok.
This generally does things like:
set the default font and size across the sheet
set up the heading row
freeze rows
In addition, I have the code below to make sure the first two columns (index 0 and 1) in the sheet are autoresizing to fit their contents. when I run it though, this element doesn't seem to make a difference. The font, column freezes etc all work.
Other notes:
I only want those 2 columns to auto-resize
the amount of rows in a sheet can vary
this job is appended to the end of several in requestList
My code:
requestList.Requests.Add(new Google.Apis.Sheets.v4.Data.Request()
{
AutoResizeDimensions = new AutoResizeDimensionsRequest()
{
Dimensions = new DimensionRange()
{
SheetId = Convert.ToInt32(sheetId),
Dimension = "COLUMNS",
StartIndex = 0,
EndIndex = 1
}
}
});
var updateRequest = sheetService.Spreadsheets.BatchUpdate(requestList, spreadSheetId);
var updateResponse = updateRequest.Execute();
Could the order which I request the 'format' changes be affecting things maybe? Can anyone help?
As written in the documentation,
the start index is inclusive and the end index is exclusive.
So, For the first two columns, it should be
startIndex = 0,
endIndex = 2
I have been checking out multiple codes on trying to update my Google Spreadsheet but have been unsuccessful when trying to do this with multiple cells. On my spreadsheet I have multiple tabs and when I update a row in column 2,3 or 4, I would like it to enter the date in column 5.
Thank you for your help.
Step 1.
In the Google Spreadsheet, click on "Script editor..." under the "Tools" menu.
Step 2.
Remove any sample script that might be in there and paste the following ...
// Sets the targetColumn on the edited row to the current date if the
// edited column in within columnBounds.
// Note: This will only handle single cell editing.
// Columns that need to be monitored for changes. Use CAPITAL letters.
var monitoredColumns = ['B', 'C', 'D'];
// Colum that will receive the date.
var targetColumn = 'E'
// To avoid adding the date in the title row, we need to consider the starting row.
var startingRow = 4
// onEdit() is a reserved function name that will be called every time the sheet will be edited.
function onEdit(e) {
var range = e.range;
// Row of the edited cell.
var row = range.getRow();
// Column of the edited cell.
var col = String.fromCharCode(64 + range.getColumn());
if (row < startingRow) {
// None of the monitored rows have been edited.
return;
}
if (monitoredColumns.indexOf(col) < 0) {
// Column B, C or D (2, 3 or 4) was not modified.
// Do not proceed any further.
return;
}
// Current spreadsheet.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// Date cell.
var dateCell = sheet.getRange(targetColumn + range.getRow());
// Set it to the current date.
dateCell.setValue(new Date());
}
Step 3
Adjust the values of monitoredColumns, targetColumn and startingRow
Step 4
Start entering some content in the cells.
I am building dynamic drop down through java code and it is working perfectly fine.
The code I am running is:
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("Data Validation");
XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper(sheet);
XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint)
dvHelper.createExplicitListConstraint(new String[]{"0000011", "0000021", "0000031"});
CellRangeAddressList addressList = new CellRangeAddressList(0, 0, 0, 0);
XSSFDataValidation validation = (XSSFDataValidation)dvHelper.createValidation(
dvConstraint, addressList);
validation.setShowErrorBox(true);
validation.setSuppressDropDownArrow(true);
sheet.addValidationData(validation);
Drop down are coming properly but when I select any one of the value from drop-down 00000 are automatically removed and only 11 is getting displayed, but I want value to be 0000011 to be selected from drop down.
drop value is showing as 0000011 but after selection it display 11. Might be if we can change cell type to text it will help or some other way but how to do it?
I solved this on my own after lot of research. so I thought to post answer to help others.
// setting cell type as string to avoid removing 00000 from drop down
CellStyle textStyle = workbook.createCellStyle();
textStyle.setDataFormat((short)BuiltinFormats.getBuiltinFormat("text"));
sheet.setDefaultColumnStyle(0, textStyle);
I would like to update specific parts of a Grid dynamically in different ways. Consider the following toy example: I have two rows: one must be updated one-by-one (a, b, c), as these symbols depend on different triggers; the second row depends on one single trigger (show) that allows displaying/hiding some data.
Now I know that I can wrap the whole Grid structure into Dynamic, and even specify which symbols to track, thus this example does what I want:
Checkbox[Dynamic[show]]
test = {0, 0};
Dynamic[Grid[{{Dynamic#a, Dynamic#b, Dynamic#c},
If[show, Prepend[test, "test:"], {}]}, Frame -> All],
TrackedSymbols :> {show}]
Though for certain reasons I would like to have a locally specified Dynamic, that is only applied to the second row of the Grid.
For those who are wondering what ungodly situation would it be, just imagine the followings: show is used in any of a, b or c, and these I do NOT want to update when show is changing, their changes depend on other triggers. Why not remove then show from the symbols of the first row? Imagine, I can't, as show is present in a function that is used in a, b or c, and this function I cannot access easily.
Of course wrapping the first argument of If into Dynamic won't help here, as the Grid itself or any of its cells won't become dynamic:
Grid[{
{Dynamic#a, Dynamic#b, Dynamic#c},
If[Dynamic#show, Prepend[test, "test:"], {}]
}, Frame -> All]
Furthermore, wrapping a row into Dynamic makes the given row invalid, as it does not have head List anymore:
Grid[{
{Dynamic#a, Dynamic#b, Dynamic#c},
Dynamic#If[show, Prepend[test, "test:"], {}]
}, Frame -> All]
Mapping Dynamic over the row does not work either because show is not updated dynamically:
Grid[{
{Dynamic#a, Dynamic#b, Dynamic#c},
Dynamic /# If[show, Prepend[test, "test:"], {}]
}, Frame -> All]
Also, wrapping Dynamic[If[...]] around list members work, but now I have to evaluate If 3 times instead of just 1.
Grid[{
{Dynamic#a, Dynamic#b, Dynamic#c},
Dynamic[If[show, #, ""]] & /# Prepend[test, "test:"]
}, Frame -> All]
Would like to know if there is any solution to overcome this particular problem by locally applying a Dynamic wrapper on a row.
Here is a solution using the Experimental ValueFunction
show = True;
test = {0, 0};
Checkbox[Dynamic[show]]
Now write your own little Dynamic update function on the side
Needs["Experimental`"];
row = {};
updateRow[x_, v_] := row = If[v, Prepend[test, "test:"], {}];
ValueFunction[show] = updateRow;
Now make the Grid, and now can use Dynamic on EACH row, not around the whole Grid, which is what you wanted:
Grid[{
{Dynamic#a, Dynamic#b, Dynamic#c},
{Dynamic#row}
},
Frame -> All
]
ps. I just read a post here by telefunkenvf14 that mentions this package and this function, which I did not know about, and when I saw this function, I remembered this question, and I thought it should be possible to use that function to solve this problem.
ps. I need to work more on placing the grid row correctly....
update(1)
I can't figure how to splice the final row over the columns in the grid. Which is strange, as it has List head, yet it won't go across all the columns. It will only go in the first cell. Tried Sequence, SpanFromLeft, and such, but no luck. May be someone can figure this part out.
Here is my current trial:
Needs["Experimental`"];
row = {};
updateRow[x_, v_] := row = If[v, {"test:", 0, 0}, {}];
ValueFunction[show] = updateRow;
show = False;
Checkbox[Dynamic[show]]
f = Grid[{
{Dynamic#a, Dynamic#b, Dynamic#c},
List#Dynamic[row]
},
Frame -> All
]
It seems it should be doable. I do not see what is the problem now...
update(2)
As a temporary solution, I split the second row by force before hand. This made it possible to do what I want. Not sure if this meets the OP specifications or not (my guess is that it does not), but here it is:
Needs["Experimental`"];
ra = 0;
rb = 0;
rc = 0;
updateRow[x_, v_] :=
row = If[v, ra = "test:"; rb = 0; rc = 0, ra = ""; rb = ""; rc = ""]
ValueFunction[show] = updateRow;
show = False;
Checkbox[Dynamic[show]]
f = Grid[{
{Dynamic#a, Dynamic#b, Dynamic#c},
{Dynamic#ra, Dynamic#rb, Dynamic#rc}
},
Frame -> All]
This is actually a comment on #Nasser's solution and suggested fix to avoid manual splitting of the second row, but because of space limitations in the comment area, I post it as answer. Will be happy to delete it as soon as Nasser confirms that it works and incorporates it into his answer.
The clue to a solution is found in the Possible Issues section of Item in the documentation:
If Item is not the top-most item in the child of a function that supports Item, it will not work.
I use this to modify #Nasser's solution in the following way. First, I need to change the definition of row so that for both values of show the length of row is the same.
Needs["Experimental`"];
row = {"", "", ""};
updateRow[x_, v_] := row = If[v, Prepend[test, "test:"], {"", "", ""}];
Experimental`ValueFunction[show] = updateRow;
The second change needed is to wrap each element of Dynamic#row with Item:
Grid[{{Dynamic#a, Dynamic#b, Dynamic#c},
{Item[Dynamic#row[[1]]], Item[Dynamic#row[[2]]],
Item[Dynamic#row[[3]]]}}, Frame -> All]
Edit: Item wrapper is not really needed; it works just as well without it:
Grid[{{Dynamic#a, Dynamic#b, Dynamic#c},
{Dynamic#row[[1]], Dynamic#row[[2]],
Dynamic#row[[3]]}}, Frame -> All]