Qtableview, add widgets between rows - qtableview

I have a QTableview (multiple columns, sorting) and would like to add a button that shows additional data below the current row. For the rendering of this additional data I would like to use another widget, that fills up a variable height and spans all the rows.
While I know that I can create delegates for cells, I was wondering if this is possible for rows or whether that would mean that I would have to inherit from a tableview and modify its paint method, which seems to be lot of work for a novice like me.

QVariant YourTableModel::data(const QModelIndex & index, int32_t role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
switch (index.column())
{
case YOUR_COL:
double theDouble = getDoubleFromModelSomewhere();
return QString::number(theDouble, 'f', 3); // Format shows 3 decimals
}
}
return QVariant();
}

If I have understood your question properly then I think this is the answer.
QTableView *view = new QTableView;
view->setItemDelegateForRow(int row, QAbstractItemDelegate *delegate);

Related

Is there a way to sort a table based on a cell value in Angular?

My current table looks like this:
Status
Draft
Pending
Complete
I want to sort them based on the value of the cells. Is there a way to do that? I've only been able to sort them using this code:
onChange(status: string){
const sortState: Sort = {active: status, direction: 'desc'};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
}
But I want to sort using the values of the status themselves since I'd want to create a button which when click sorts starting from complete or draft or pending.
I'm a little confused by your question, but I think I understand what you're asking.
You're going to want to convert your values into an array and then use the .sort() function. So, assuming you have an array of your cells, we can call that let array = Cell[], you can then access the status of the cells like this:
sortCells(){
let array = Cell[]; // here we're assuming there is already a cell type and a cell.active parameter, like shown in your example.
let possibleValues = ["Draft","Pending","Complete"]; // easier way to compare two values
array.sort((a,b)=>{
let aIndex = possibleValues.indexOf(a.active); // index of gets the location of the element in an array
let bIndex = possibleValues.indexOf(b.active);
if(a > b){
return -1;
} else if(b > a){
return 1;
}else{
return 0; // they are equal
}
})
}
You can read more about sort here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

How to provide multiple links in one cell of a QTableView

I have a QTableView in my project, in which several columns display data that includes a hyperlink. I use a delegate class for these to set it up so that when the cell in the column is clicked, it opens the linked page in the browser. This works great... when it's only one value being linked to one page. For example, I may have a list of search values for mysite.com where the columns have values A, B, C, etc.. If the user clicks on the cell in this column with A, it will open a hyperlink for mysite.com/A (again, this part works fine). However, I now need to add a column that may have something like "A,B", where it needs to support links to search for A AND B in the same cell depending on which they click. I've been searching around online for a while now and it seems like this probably can't be done with a delegate. I have a line in a QTextBrowser elsewhere in my code where I can do this via HTML, like this:
QString toShow;
for(int i = 0; i < searchValueList.size(); i++)
{
toShow.append("`<a href=\"www.mysite.com/" + searchValueList.at(i) + "\"`>" +
searchValueList.at(i) + "`</a`>";
}
However I can't find any way to set the cells in a QTableView to recognize HTML formatting or Rich Text, and alas I'm not even sure that's possible. Is there any way at all to do what I'm trying to accomplish?
You can create a custom QItemDelegate for the specific column in which you can display rich text. The delegate could be like :
class RichTextDelegate: public QItemDelegate
{
public:
RichTextDelegate(QObject *parent = 0);
void paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const;
};
RichTextDelegate::RichTextDelegate(QObject *parent):QItemDelegate(parent)
{
}
void RichTextDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if( option.state & QStyle::State_Selected )
painter->fillRect( option.rect, option.palette.highlight() );
painter->save();
QTextDocument document;
document.setTextWidth(option.rect.width());
QVariant value = index.data(Qt::DisplayRole);
if (value.isValid() && !value.isNull())
{
document.setHtml(value.toString());
painter->translate(option.rect.topLeft());
document.drawContents(painter);
}
painter->restore();
}
You should set the item delegate for the specific column :
ui->tableView->setItemDelegateForColumn(colIndex, new RichTextDelegate(ui->tableView));
Now if you set the model text for the specific column in a row to a rich text, it will be shown properly :
model->item(rowIndex, colIndex)->setText(someRichText);

Create a Non-Database-Driven Lookup

Lots of references for creating lookups out there, but all seem to draw their values from a query.
I want to add a lookup to a field that will add items from a list of values that do not come from a table, query, or any other data source.
Such as from a string: "Bananas, Apples, Oranges"
..or a container ["Bananas", "Apples", "Oranges"]
Assume the string/container is a dynamic object. Drawing from an static enum is not a choice.
Is there a way to create lookups on the fly from something other than a data source?
Example code would be a great help, but I'll take hints as well.
There is the color picker.
Also in the Global you will find pickXxxx such as pickList.
There are others, pickUser, pickUserGroup etc.
Take a look on the implementation. I guess they build a temporary table then displays that. Tables are great!
Update:
To go on you own follow the rules.
For the advanced user, see also: Lookup form returning more than one value.
public void lookup()
{
SysTableLookup sysTableLookup;
TmpTableFieldLookup tmpTableFieldLookup;
Enumerator en;
List entitylist = new list(types::String);
entitylist.addend("Banana");
entitylist.addend("Apple");
en = entityList.getEnumerator();
while (en.moveNext())
{
tmpTableFieldLookup.TableName = en.current();
tmpTableFieldLookup.insert();
}
sysTableLookup = SysTableLookup::newParameters(tableNum(tmpTableFieldLookup), this);
sysTableLookup.addLookupfield(fieldNum(TmpTableFieldLookup, TableName));
//BP Deviation documented
sysTableLookup.parmTmpBuffer(tmpTableFieldLookup);
sysTableLookup.performFormLookup();
}
The above code helps in displaying strings as lookup.
I'm also guessing there's no way to perform a lookup without a table. I say that because a lookup is simply a form with one or more datasources that is displayed in a different way.
I've also blogged about this, so you can get some info on how to perform a lookup, even with a temporary table, here:
http://devexpp.blogspot.com.br/2012/02/dynamics-ax-custom-lookup.html
Example from global::PickEnumValue:
static int pickEnumValue(EnumId _enumId, boolean _omitZero = false)
{
Object formRun;
container names;
container values;
int i,value = -1,valueIndex;
str name;
#ResAppl
DictEnum dictEnum = new DictEnum(_enumId);
;
if (!dictEnum)
return -1;
for (i=1;i<=dictEnum.values();i++)
{
value = dictEnum.index2Value(i);
if (!(_omitZero && (value == 0)))
{
names += dictEnum.index2Label(i);
values += value;
}
}
formRun = classfactory.createPicklist();
formRun.init();
formRun.choices(names, #ImageClass);
formRun.caption(dictEnum.label());
formRun.run();
formRun.wait();
name = formRun.choice();
value = formRun.choiceInt();
if (value>=0) // the picklist form returns -1 if a choice has not been made
{
valueIndex = -1;
for (i=1;i<=conLen(names);i++)
{
if (name == conPeek(names,i))
{
valueIndex = i;
break;
}
}
if (valueIndex>=0)
return conPeek(values,valueIndex);
}
return value;
}
It isn't the most graceful solution, but this does work, and it doesn't override or modify any native AX 2012 objects:
Copy the sysLookup form from AX2009 (rename it) and import it into AX 2012.
We'll call mine myLookupFormCopy.
I did a find/replace of "sysLookup" in the XPO file to rename it.
Create this class method:
public static client void lookupList(FormStringControl _formStringControl, List _valueList, str _columnLabel = '')
{
Args args;
FormRun formRun;
;
if (_formStringControl && _valueList && _valueList.typeId() == Types::String)
{
args = new Args(formstr(myLookupFormCopy));
args.parmObject(_valueList);
args.parm(_columnLabel);
formRun = classFactory.formRunClass(args);
_formStringControl.performFormLookup(formRun);
}
}
In the lookup method for your string control, use:
public void lookup()
{
List valueList = new List(Types::String);
;
...build your valueList here...
MyClass::lookupList(this, valueList, "List Title");
super();
}

Limit Flex Spark DataGrid ItemRenderer data to column

I've got a DataGrid in my mobile application (I know, I know but there's currently not other solution for this), that numeric values. Depending on the value of a cell, the text gets colored. My big issue is that this doesn't work very for more than about 5 rows of data. I reckon the issue is that the set text function (in which I format the color) can't keep up with the amount of cell changes and formats the text based on the last updated cell of the row.
I thought about a column item renderer so that the renderer only gets the value for that column, and not the whole data of the row.
Is something like that possible?
Just for reference, this is my current item renderer (again, this works fine for few rows, for 5+ of fast changing data, this doesn't work any more, cells get formatted even though their data hasn't changed).
public class ColorGridItemRenderer extends DefaultGridItemRenderer
{
public function ColorGridItemRenderer() {
super();
}
override public function set text(value:String):void {
if (!value)
value = "";
if(Number(value) > Number(text) && text!="")
setStyle("color", 0x40c040);
else if(Number(value) < Number(text) && text!="")
setStyle("color", 0xf05050);
else
setStyle("color", 0xc1c1c1);
}
super.text = value;
}
}
Edit:
I just double-checked and have to revert my previous statement. A single row works fine, the second row already messes up the color formatting though. It seems as if the datagrid somehow throws together the values.
Doing the color formatting in the set data method shows the exact same effect;
override public function set data(value:Object):void {
if(value && data) {
label = value[column.dataField];
if(value[column.dataField] > _oldVal && _oldVal != 0)
setStyle("color", 0x40c040);
else if(value[column.dataField] < _oldVal)
setStyle("color", 0xf05050);
else
setStyle("color", 0xc0c0c0);
_oldVal = value[column.dataField];
}
super.data = value;
}
Edit2:
My assumption about the grid messing up the ItemRenderer's data seems to be correct.
A simple trace in the set data method (trace("old: " + _oldVal + "new : " + value[column.dataField]);) revealed that somehow, values of the next grid row (in case there are 2) get used as well as _oldVal gets the old value of the next row.
Just for reference:
Calling super on the overridden function sets the value to the superclass, thus mixing the values of the rows. Leaving it from the function solves the problem.

Show "no rows found" inside a JTable row if not found entry while filtering

i have a JTable with row filter.
once fitered if i didn't get any row then i have to show a string like "Nothing found to display " inside table as first row.
please do needful.
Thanks ,
Narasimha
I needed to solve this exact problem today and wasn't able to find a good answer online. In the end I came up with my own solution, which I think may be exactly what you want. I don't know if this is too late for your project, but perhaps it can help others:
JTable.paintComponent(Graphics g) will not be called unless the table has a height that is greater than 0. This causes a problem for empty tables, so we expand the JTable height if needed so that it will always be at least the size of the JViewport that is it's parent.
JTable tableName = new JTable() {
public boolean getScrollableTracksViewportHeight() {
if(getParent() instanceof JViewport)
return(((JViewport)getParent()).getHeight() > getPreferredSize().height);
return super.getScrollableTracksViewportHeight();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(getRowCount() == 0) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
g2d.drawString("Nothing found to display.",10,20);
}
}
}
containerName.add(new JScrollPane(tableName));
To show a line of text in the multicolumn table can be quite difficult, the span AFAIK is not supported.
One possibility would be to hide all data columns (to show them later you have to memorize them somewhere) and show one column for the message.
An easier way would be to create a JPanel with CardLayout, add 2 cards - one containing table and one containing your empty data warning. If the filter returns empty result, show the card with empty warning, in other case - show the table.
If the filter has excluded all rows, the result returned by getViewRowCount() will be zero. You can update the GUI accordingly; setToolTipText() is handy if screen space is lacking.
Here's an example:
TableModel model = ...
JTable table = new JTable(model);
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model);
table.setRowSorter(sorter);
JScrollPane pane = new JScrollPane(table);
JLabel label = new JLabel("");
...
String s = "Rows: " + sorter.getViewRowCount()
pane.setToolTipText(s);
label.setText(s);