From what I have seen, Qt documentation and majority of examples online assume that we are happy with (column, row)-based lookup in data(). But what If my table is based on a custom structure? For instance let's have:
struct MyDrive
{
QString serialNo;
QString user;
QString pc;
QString ipAddress;
QString category;
};
where serialNo is the key. So any operation from outside (imagine the model having implemented a listener) uses it for removing/modifying an item, making QMap as an ideal candidate.
But how to connect this structure with QModelIndex's data? QAbstractTableModel::data asks for data with (column,row) as key, making it more suitable for QVector<QVector>> or something similar (somewhere I read I should avoid using containers with non-constant access time (like map) in data()).
I can imagine using, well, a map with QModelIndex as key and serialNo as value, which would be used as key to my (serialNo-based) map but this looks very inefficient -- QModelIndex addresses concrete entry (serialNo, user, pc, ...) after all so we'd be duplicating the same item over and over again. I was also thinking about having a <serialNo, MyDrive*> map but this is just a workaround to an ugly design decision.
I can't believe I'm the first one with this scenario, so how is it usually solved?
You can use QAbstractItemModel::match to find items by serial
qt help
And enter all the necessary data into the table. This will allow you not to use containers, but how efficient is this a question...
Second solution is subclass AbstractItemModel Reference. Now you can do what you want and use any container by implementing data() function.
Related
I have a config object called config represented with a QQmlPropertyMap. It has a key called huge_list which is a large list of strings.
QVariantList hugeList = makeHugeList(); // makes a huge list of QStrings
QQmlPropertyMap config;
ownerData.insert("huge_list", hugeList);
How can I add, remove, or edit a particular element of huge_list in C++ without replacing the entire list with a new one? The issue is that config["huge_list"] would return a QVariant which does not seem to support mutation of the underlying data.
QHash<QPair<QString N_id, QString A_id>, QString name> info
I have this QHash , and i have the values of N_id and name for a particular index, how can i obtain the value of corresponding A_id. I am trying to use STL-style iterator. I can change QHash to QMap if needed but I cannot use:
QHash<QPair<QString N_id, QString name>, QString A_id>
Edit: N_id and A_id together forms a unique key in my case.
I think the major problem here is that QHash, being a hash table, looks up the values by hashing the keys. Hence, it needs to complete key to be able to look up a value; a "partial" key won't suffice - there's going to be no concrete object to hash then. A similar problem arises with a map: to navigate the BST, you need the complete object in order to make comparisons and left / right decisions. Thus, short of going back to the drawing board and revising your approach, I'd say, maintain a backwards map, be it a QHash or a QMap, with the mapping name -> pair(n_id, a_id). The downside is that you're going to have to keep the two in sync.
However, with the existing data structure, I'd perform a query like this:
#include <algorithm>
QHash<QPair<QString, QString>, QString> info;
QString a_n_id {/*...*/}; // the target N_id
QString a_name {/*...*/}; // the target name
/* ... */
const auto keyList = info.keys(a_name); // QList<QPair<QString, QString> >
std::find_if(keyList.begin(), keyList.end(),
[&](decltype(info)::key_type& key) { return key.first == a_n_id; });
See this question in case decltype(info)::value_type refuses to build on Microsoft VS.
This is of course going to be linear, since, as I've already said, a hash needs the complete object to be able to perform a lookup, hence we can't use the logarithmic complexity lookup in this case.
I'm learning SQLite and C++ within the Qt framework. As a learning project I am doing a simple image viewer which enables the user to tag images with keywords, categories, comments and ROI (for some later OpenCV functionality). It's a pretty simple database with some primary tables and some relational tables.
I think I've got the basics down and early tests show that my record data is being stored but in writing the methods to manage the database it looks like I am going to end up with a great many methods, particularly when I start to add in searching, sorting, deleting, etc.
This leads me to ask if I am going about this the correct way. I can see how some of my query logic (for search, sort) is probably better off being in a different "controller" type class and that all this manager class needs to do is handle the basic creation and deletion tasks, and just return a query object in response to a SELECT statement passed in as a string.
So, am I going about this in a reasonable way?
Manager methods so far:
bool openDatabase(QString name);
void closeDatabase();
bool createTables();
bool addKeyword(QString keyword);
bool addCategory(QString category);
bool deleteKeyword(QString keyword);
bool deleteCategory(QString category);
bool addROI(int x, int y, int width, int height);
bool deleteROI(int id);
bool addImage(QString name, QString path, QByteArray image, QByteArray thumb);
You probably should be using Qt's Model View Framework. The important classes there are QSqlQueryModel, QSqlTableModel and QSqlRelationalTableModel. To keep your UI isolated from the database structure, a reasonable approach would be to add view models for the particular use cases you have. You can then easily link those to, say, a Qt Quick based user interface.
There's nothing particularly wrong with function-oriented interface that you propose, except that it requires a lot of boring glue code to use it for user interfaces. It's best to factor such glue code as a proxy view model, since you're working to a documented API that can be then easily picked up by coworkers.
I have seen a DatabaseManager class somewhere and modified it. This is the insert function :
bool DatabaseManager::insert(const QString &tableName, const QString& columns, const QString &value)
{
bool ret = false;
if(db.isOpen()){
QSqlQuery query;
ret = query.exec( QString("INSERT INTO %1 (%2) VALUES(%3)").arg(tableName, columns, value) );
}
return ret;
}
You give it your table's name, the columns you want to fill and then the values. This is an example where I call it :
if( !dm.insert("Product", "ID, Name, Price, Notes, Category",
"'1', 'A DVD','10€', 'Some Notes', 'DVD'") )
{
//Note that each value is surrounded by an apostrophe
//Now whatever you want to
}
In our current project, we need some high level DBI for different databases. It should provide the following features:
in memory cache - the DBI should be able to cache all reads, and update the cache on writing calls (the application we are coding on is heavy threaded, and needs fast access to the current data all the time). The memory cache will be based on boost::multi_index
automatic sql building - We don't want to parse a sql statement to lookup in the memory cache
As we need to provide functions for: defining a table layout, do selects, do inserts, do updates, joins, ..., the interface will get very complex.
We need a good way to invoke the interface function.
There are many styles around, but we could not find any useful for our usage.
Here a few examples:
SOCI
sql << "select name, salary from persons where id = " << id, into(name), into(salary);
We don't want some SQL statements, so we would have to define what and from a different way.
pqxx
Conn.prepare("select_salary",
"select name, salary from persons where id = $1")
((string)"integer",prepare::treat_direct);
The heavy usage of the overloaded operator() is just ugly, but it could work for us too.
Any suggestions how to design the interface?
How about using object relational mapping? Here's some code fragment ideas off the top of my head - I've only done this in Python, never in C++, and only for fairly simple databases. There's a list of frameworks on Wikipedia that should avoid too much wheel-related R&D.
class people: public dbi_table
{
// id column handled by dbi_table.
name: string_column;
salary: money_column;
};
class cost_center: public dbi_table
{
name: string_column;
office: foreign_key<offices>;
};
class people_cost_center_link: public link_table
{
// Many-many relationships.
};
Then you can manipulate records as objects, all the relational stuff is handled by the framework. Querying is done by defining a query object and then getting an iterator to the results (see the ODB wikipedia page for a code example).
I would do it like this (and it'd be good in c++ point of view, dunno if it's correct database stuff):
struct Handle { int id; }
class DBI
{
public:
virtual Handle select(int column_id)=0;
virtual Handle select(int column1, int column2)=0;
virtual Handle id(int id)=0;
virtual Handle join(Handle i1, Handle i2)=0;
virtual void execute_query(Handle i)=0;
};
Usually these functions would be implemented like this:
Handle select(int column_id) {
return new_handle(new SelectNode(column_id));
}
where new_handle function would just insert SelectNode to std::vector or std::map and create an handle for it.
The diagram http://www.freeimagehosting.net/uploads/2fd3f4161c.png
Here's the Minimalist-UML diagram of an app I've been working on. It's supposed to simulate the management of a bunch of sensors relating to different measurements.
Please ignore the House class, the diagram is outdated...
However, I have trouble. Each sensor (sound, contact, heat, pressure, gas - All of these inherit from sensor) has an unique ID, starting at 0 for the first one and ending at the total number of sensors - 1.
For good practices' sake, where shall I store the total number of sensors, so the classes I'm using for input/output of files (saving and loading) and insertion of new sensors can access and increment that counter?
Thank you for your time!
One option would be to create a static function in your Sensor class that increments and returns a static counter variable. The constructor for Sensor could call this function to get an ID.
// header file
class Sensor
{
...
protected:
static int getId() { return id++; }
private:
static int id;
int myId;
};
// .cpp file
int Sensor::id = 0;
Sensor::Sensor(...)
: myId(getId())
...
{}
I'm ignoring threading and persistence issues here. But hopefully this gives you a better idea.
Whatever object creates the sensors should assign the identifiers to the sensors.
If multiple objects create sensors, then they should be assigned a pointer or reference to a provider of identifiers when they are created and they should query that provider for a new unique identifier as they create new sensor objects.
Your unique ID, like a database table ID will likely have some issues.
You will probably find, eventually, that your ID needs to persist across sessions--that your window's ID is used in some other relationship.
You may, some day, also find that it needs to be unique across multiple server/client sets.
I'm just suggesting that you should consider these issues off the bat.
As for where the ID should be generated, since all your "Sensor" classes inherit from one base class, I think I'd generate it via a threadsafe method in that base class--and I'd store it there as well.
what's the problem? do you use a Vector to store your sensors? define a Vector of holding sensor-objects in the house.
can access and increment that counter
you don't have to do this, the Vector does it for you
Have a look at the Singleton pattern assuming you don't want to do it with a database of some sort.