I am using ActiveX to kick Excel from Qt and I retrieved the data array using a specific range. The datatype is QVariant but in the debugger it is written "QVariant(QVariantList)". It has around 1000 items, how can I access/get these items ?
Here is the code I produced.
QAxObject* excel = new QAxObject( "Excel.Application", 0 );
QAxObject* workbooks = excel->querySubObject( "Workbooks" );
QAxObject* workbook = workbooks->querySubObject( "Open(const QString&)", filename );
QAxObject* sheets = workbook->querySubObject( "Worksheets" );
QAxObject* sheet = sheets->querySubObject( "Item( int )", 1);
QAxObject* range = sheet->querySubObject("Range(A2:A1002)");
QVariant data = range->dynamicCall("value"); // the data I want to retrieve
QVariant data = range->dynamicCall("value");
QVariantList list = data.toList();
for (/*const*/ auto &v : list) {
/// work with QVariant v
}
From the Qt Docs:
The QVariant class acts like a union for the most common Qt data
types.
"most common Qt data types" includes QVariantList. So if you are sure that your QVariant is of type QVariantList, you can use the member function of QVariant to cast it to a QVariantList. If you are not entirely certain that your QVariant is actually a QVariantList, you should use QVariant.canConvert(targetTypeId) to verify before you call QVariant.toList().
Related
I am using Qt5 and errors exist from this line of codes after running sample project I found on the internet.
QFile f( "world.txt" );
if( f.open( QIODevice::ReadOnly ) ) {
QTextStream ts( &f );
Vertex v[3];
int vcount = 0;
bool allok, ok;
while( !ts.atEnd() )
{
QStringList line = QString::split( " ",ts.readLine().simplifyWhiteSpace() );
Errors are:
split is not a member of QStringList
simplifyWhiteSpace is not a member of QString
I don't know how to convert the line to work on Qt5.
Both QStringList::split() and QString::simplifyWhitespace() were functions in Qt3, and have been renamed or moved for Qt5 (which you are using according to your tags).
For QStringList::split(), the documentation says:
Use QString::split(sep, QString::SkipEmptyParts) or QString::split(sep, QString::KeepEmptyParts) instead.
Be aware that the QString::split()'s return value is a QStringList that always contains at least one element, even if str is empty.
You already changed this in your edit, so you are left with QString::simplifyWhitespace(), where the documentation says:
QString QString::simplifyWhiteSpace () const
Use simplified() instead.
In a column in a QTableWidget I want to display some double values. By doing the following, I get my desired display:
double value = 1234.567;
QTableWidgetItem* qti = new QTableWidgetItem(QString::number(value , 'f', 4));
Now, if I enable sorting on the table and sort the values in this column, it will sort as strings. So 90.0000 will come be "larger" than 800.0000 for example (9 > 8).
If I do this instead:
QTableWidgetItem* qti = new QTableWidgetItem();
qti->setData(Qt::DisplayRole, value);
or
QTableWidgetItem* qti = new QTableWidgetItem(QString::number(value , 'f', 4));
qti->setData(Qt::DisplayRole, value);
I can sort my column correctly, but I "lose" the formmating (12.0000 is displayed as 12).
I've also tried like this:
QTableWidgetItem* qti = new QTableWidgetItem();
qti->setData(Qt::UserRole, value);
qti->setData(Qt::DisplayRole, QString::number(value, 'f', 4));
How can I format the display of the values, while still enabling sorting?
(In all code snippets above, the QTableWidgetItems' are added by:
table->setItem(rowNumber, colNumber, qti);
where table is a QTableWidget)
You do it wrong. Let's do it right. The solution is to subclass QTableWidgetItem.
class CustomTableWidgetItem : public QTableWidgetItem
{
public:
CustomTableWidgetItem(const QString txt = QString("0"))
:QTableWidgetItem(txt)
{
}
bool operator <(const QTableWidgetItem& other) const
{
qDebug() << "Sorting numbers";
return text().toFloat() < other.text().toFloat();
// text() is part of QTableWidgetItem, so you can write it as QTableWidgetItem::text().toFloat() as well
}
};
Example insertions
ui->tableWidget->setSortingEnabled(false);
ui->tableWidget->insertRow(0);
ui->tableWidget->setItem(0, 0, new CustomTableWidgetItem(QString::number(90.0005, 'f', 4)));
ui->tableWidget->insertRow(1);
ui->tableWidget->setItem(1, 0, new CustomTableWidgetItem(QString::number(800.0003, 'f', 4)));
ui->tableWidget->insertRow(2);
ui->tableWidget->setItem(2, 0, new CustomTableWidgetItem(QString::number(200.0010, 'f', 4)));
ui->tableWidget->insertRow(3);
ui->tableWidget->setItem(3, 0, new CustomTableWidgetItem(QString::number(200.0020, 'f', 4)));
ui->tableWidget->setSortingEnabled(true);
Sorting behaviour is as expected
ascending
90.0005
200.0010
200.0020
800.0003
descending
800.0003
200.0020
200.0010
90.0005
PS: Remember to turn off sorting before you insert bew items.
Another way of doing this is by using QStyledItemDelegate attached to the double column(s). It need not do much except implement the QStyledItemDelegate::displayText() function.
For further details, see QTableView and display of doubles at Qt Centre.
Here's what I came up with, based on Mangesh's answer, for a similar problem displaying a percentage:
class percentageItemC : public QStyledItemDelegate
{
QString displayText(const QVariant &value, const QLocale &locale) const
{
return QString::asprintf("%0.3lf%% ", value.toDouble());
}
} percentageItem;
That's any number of digits to the left of the decimal, 3 to the right, and a long float with a percent sign. Then, do a
ui->twMyTable->setItemDelegateForColumn(1, &percentageItem);
in yer form's class. I can also re-use them for other columns, and presumably rows.
This method doesn't slow things down with extra text to float conversions. You can also have nice extras, like commas, percent signs and currency signs in it. I have this for currency:
class currencyItemC : public QStyledItemDelegate
{
QString displayText(const QVariant &value, const QLocale &locale) const
{
return locale.toCurrencyString(value.toDouble()) + " ";
}
} currencyItem;
I use:
QTableWidgetItem *item = new QTableWidgetItem();
item->setData(Qt::EditRole, number);
to create the table items. It is smart about these: it creates a QVariant, and then sorts based on the type. This way, you don't have to set the underlying data to a string, but it can be any type it has a sort for.
The columns sort numerically like they're s'posed to, but they display in any format ya like. ;}
From my point of view the best option to solve this problem is to create new class with usage of setData() with Qt::UserRole to store unformatted data. In this setup you can store real data and text together. This has more memory consumption but you doesn't need to convert from string to int/float/etc.
I've tested many other roles Qt::EditRole, Qt::DisplayRole, etc and they are overwriting stored data/text and you can't store both values.
Here is my final solution of class displaying % percent values (python code)
from PyQt5 import QtWidgets, QtCore
class PercentTableWidgetItem(QtWidgets.QTableWidgetItem):
# Float value
value: float = 0
def __init__(self,
value: float = 0
):
''' Constructor.'''
super().__init__()
# Set variables
self.value = value
# Set item data
self.setData(QtCore.Qt.UserRole, value)
self.setText(f'{self.value:.2f}%')
def __lt__(self, other: QtWidgets.QTableWidgetItem):
''' Operation < for sorting.'''
value = other.data(QtCore.Qt.UserRole)
return (value is not None) and (self.value < value)
Usage in code as simple as below
table = QTableWidget()
table.setItem(0,0,PercentTableWidgetItem(35.67))
I am writing data to a config file using the following code.
QSettings settings("/root/configFile.ini",QSettings::IniFormat);
QString userName = lineEditUsername.text();
QString password = lineEditPassword.text();
QList<QString> listUsername;
QList<QString> listPassword;
settings.beginWriteArray("UserData");
for(i=0;i<listUsername.size();i++)
{
Qstring user = listUsername.at(i);
Qstring pass = listPassword.at(i);
settings.setArryIndex(i);
settings.setValue("Username",user);
settings.setValue("Password",pass);
}
settings.endArray();
}
Now when I run the code first time and give 4 or 5 values they are formed in proper order in the file. However if I run the application for second time the values start overwriting from first position. Can some one suggest me some solution for this?
Instead of creating and maintaining arrays and indexes, I would propose to create user credentials map and store it in the settings file as follows:
QSettings settings("/root/configFile.ini", QSettings::IniFormat);
QString userName = lineEditUsername.text();
QString password = lineEditPassword.text();
QList<QString> listUsername;
QList<QString> listPassword;
//settings.beginWriteArray("UserData");
QVariantMap userDataMapping;
for(int i = 0; i < listUsername.size() ; i++)
{
QString user = listUsername.at(i);
QString pass = listPassword.at(i);
userDataMapping[user] = pass;
//settings.setArryIndex(i);
//settings.setValue("Username",user);
//settings.setValue("Password",pass);
}
// Store the mapping.
settings.setValue("UserData", userDataMapping);
//settings.endArray();
// ...
This will store your data in ini file in the following format:
UserData=#Variant(\0\0\0\b\0\0\0\x1\0\0\0\x6\0\x64\0\x64\0\x64\0\0\0\n\0\0\0\x6\0\x62\0\x62\0\x62)
When you read settings, do something like this:
[..]
QVariant v = settings.value("UserData");
QVariantMap map = v.value<QVariantMap>();
QMapIterator<QString, QVariant> i(map);
while (i.hasNext()) {
i.next();
QString user = i.key();
QString pass = i.value().toString();
}
You need to retrieve the amount of existing entries before adding a new one. Something like this:
int size = settings.beginReadArray( "UserData" );
settings.endArray();
settings.beginWriteArray( "UserData" );
settings.setArrayIndex( size ); // Note: Maybe 'size - 1', not sure
// ...
settings.endArray();
setArrayIndex( size ) will move the array index to the end and will thus no longer override an existing entry
I am new with QT, so I'll apprciate any help.
In my application, I'm creating a QStandardItemModel with rows and columns. Now I want to save in one cell a list of QStrings - but I dont know how to do that.
I've tried to write this code:
QStandardItem* dataRecords = new QStandardItem();
QList<QStandardItem* > list;
QList<QString>::const_iterator dataRecord;
for( dataRecord = i.value()->begin(); dataRecord != i.value()->end(); ++dataRecord )
list << new QStandardItem((*dataRecord));
dataRecords->appendRows(list);
model->setItem(row, 3, dataRecords);
i is a QList of QString.
Now, I dont know how can I access abd retrive the QString values from the model.
Can anyone please help me? or suggest me another way to do that?
Thanks!
You should read some docks about Model\View proggramming in Qt
To access data stored in model you should use: QVariant QStandardItemModel::data ( const QModelIndex & index, int role = Qt::DisplayRole )
To get QModelIndex for particular cell use: QModelIndex QStandardItemModel::index ( int row, int column, const QModelIndex & parent = QModelIndex() )
Some code example...
QModelIndex superIndex = model->index(i,j);
QString superData= model->data(superIndex).toString();
My task is custom sorting items in QStandardItemModel.
By default for a sequance of rows
text1
text11
text12
text100
text110
text120
text1110
function QStandardItemModel::sort() sorting it as
text1
text100
text11
text110
text1110
text12
text120
I want that would be
text1
text11
text12
text100
text110
text120
text1110
For this purpose I overload function int QString::compare(const QString &s) const in the separeted compare.cpp file.
int QString::compare(const QString &s) const
{
QString strL = "";
strL.append(this);
QString strR = "";
strR.append(s);
QStringList list = strL.split(QRegExp("\\D+"),QString::SkipEmptyParts);
foreach (QString num, list) {
strL.replace(num, QString("%1").arg(num,10,'0'));
}
list = strR.split(QRegExp("\\D+"),QString::SkipEmptyParts);
foreach (QString num, list) {
strR.replace(num, QString("%1").arg(num,10,'0'));
}
return strL.localeAwareCompare(strR);
}
that using in operator
virtual bool operator< ( const QStandardItem & other ) const.
Such function as compare can be redefined in the separate file and it is simple to add it in *.pro and easy application will find its realization. But with more difficult applications such way it is impossible. Please tell me Why?
Example : code
Rather than using a QStandardItemModel directly, you want to wrap it in a QSortFilterProxyModel. This class was designed for exactly the situation you describe--when you want to implement custom sorting or filtering behavior. Just implement the QSortFilterProxyModel::lessThan method to reflect the desired behavior.
QStandardItemModel has a virtual function sort(int column, Qt::SortOrder order = Qt::AscendingOrder). I think it will be easier to subclass QStandardItemModel and reimplement sortfunction.
I did!
When i said "heavy application", i means application, that contains and binds many other plugins and libs. And for this purpose what I create the new lib with name QStringCompare, that contains one file compare.cpp with my new definiotoin of compare:
#include <QStringList>
#include <QRegExp>
int Q_DECL_EXPORT QString::compare(const QString &s) const
{
QString strL = "";
strL.append(this);
QString strR = "";
strR.append(s);
QStringList list = strL.split(QRegExp("\\D+"),QString::SkipEmptyParts);
foreach (QString num, list) {
strL.replace(num, QString("%1").arg(num,10,'0'));
}
list = strR.split(QRegExp("\\D+"),QString::SkipEmptyParts);
foreach (QString num, list) {
strR.replace(num, QString("%1").arg(num,10,'0'));
}
return strL.localeAwareCompare(strR);
}
and it links QStringCompare.lib to *.pro of Main apllication of my project. Generally it's not necessary to declare it in *.h files. All other plug-ins inherit this redefinition. The experiments showed that it is necessary to links to main application.
As that so.
There can be I am mistaken in reasonings, but it's working on linux and windows.
This is source of QStringCompare.libs. You can try.