I am instrumenting a model/view for item rearrangement and I am failing to understand how to override the insertRows method. Just for practice I am trying to wrap a std::vector with a custom structure.
std::vector<aStruct> mD;//my data
bool insertRows(int start, int rows, const QModelIndex & parent)
{
auto i = parent.row();
cout <<"I going to " << start << ":" << rows << " choosing "<< i<< endl;
beginInsertRows(parent, start, start + rows - 1);
aStruct blank(0);// Should be the value of the item being moved?
mD.insert(mD.begin()+start,blank);
endInsertRows();
return true;
}
Unfortunately, I can't seem to find a place to get at element that gives me a hold of the item being moved. How do I do this?
I assume the mD and insertRows are members of a custom model class.
insertRows doesn't receive any information about contents of inserted rows. Empty values should be inserted. Rows should be filled with data in the setData virtual method implementation.
You should use next steps to insert rows:
1. Call beginInsertRows
2. Modify your internal data structure
3. Call endInsertRows
There is all Ok in your sample.
View will insert empty rows (as #Riateche told) and will fill it automatically, after call of endInsertRows. All that you need is to override YourModel::data method to return correct data from you mD structure.
View will call YourModel::data method immediately after inserting empty rows. You don't need to do any extra operations. View will care about "filling" it.
Overriding of YourModel::setData method is mostly used for interaction between view and model, when user want to change data throught view widget.
Related
I've been reading about keeping functions simple and specific to only one purpose. And that even a function that does a simple calculation and print out the result is already too much.
I've been working on an item shop system for a small game. The shop has a vector of all items available in the game. Each Item keeps track of its own count (is that bad?), and do not show up in the inventory output if its count is zero.
When a player (rider) wants to buy an item, the shop should check if the player has enough credit and if such item is in stock before the item gets removed from the shop (and then added to the player's inventory).
I have combined all of that into one function, thinking that this avoid repetition of traversing through the vector of Items.
/* ItemShop.cpp
* non-binary boolean:
* returns 0 if item is not in stock
* returns 1 if rider has insufficient creds to buy the item
* returns 2 if purchase is successful */
int ItemShop::buyItem(const string& itemName, const int cred, Item& newItem) {
// go through shop inventory and search for item with given name
for (int i = 0; i < items.size(); i++) {
if (items[i].getName() == itemName) {
if (items[i].getCount() > 0) { // item is in stock
if (items[i].getValue() <= cred) { // rider has enough creds to buy the item
newItem = items[i]; // pass a copy of the item by ref
newItem.setCount(1); // take one of that item
items[i].removeOne(); // remove one from the shop
return 2; // purchase is successful
}
return 1; // rider cannot afford to buy the item
}
}
}
return 0; // item is not in stock
}
In doing this approach, I end up with a larger multi-purpose function but I didn't have to go through the Item vector multiple times. I think if I were to break the function into separate ones, they would be:
check if item is in stock
check if player can afford
transaction
Each of those functions would have to go through and find the item in the vector (unless maybe I pass a reference of it.. from the function?).
All in all, does less code repetition make my approach justifiable? If not, how should I break it down?
Two recommendations:
Store your items in a std::map<string,Item> where the string is
the item name. This will remove the search loop.
Use an enum as return value instead of an int.
You could also implement simple functions for the different checks, but that's a matter of taste I would say.
You can break that up into the following operations:
find the appropriate item by name (that's what the loop mainly does) and return a reference to the found item
check if there are enough items in stock. item itself already carries that information as it looks, it doesn't need to be executed inside the loop
check if the player can afford it. You already have the cred value apparently, it doesn't need to be executed inside the loop
if the latter two conditions are fulfilled do the transaction, again it doesn't need to be executed inside the loop
Your function doesn't do too many things - you are purchasing an item, which includes those three necessary steps (btw.: aren't you missing the payment?).
However (aside from other things that could be improved), you should make sure that the steps don't get intermingled. In particular avoid nested structures whenever possible.
You could e.g.rewrite your body (without changing the interface) like this:
int ItemShop::buyItem(const string& itemName, const int cred, Item& newItem) {
//find item
auto it = std::find_if(items.begin(), items.end(), [&](const Item& item) {return item.getName() == itemName; });
//Check if Item can be purchased
if (it == items.end() || it->getCount == 0) {
return 0; //item is not in stock
}
if (it->getValue() > cred) {
return 1; //rider can't afford item
}
//buy item
newItem = *it; // pass a copy of the item by ref <- Avoid that if possible
newItem.setCount(1); // take one of that item
it->removeOne(); // remove one from the shop
return 2; // purchase is successful
}
I do think an item keeping its own count is a poor idea. An "inventory" (or something on that order) keeps track of the items in stock and the number of each. An item should be just that: an item.
I think the suggestion to use a [unordered_]map is a good one. It pre-implements what is probably the single most complex part of your current function (though none of it is particularly complex).
Do note that some of this can get much trickier when/if multiple threads get involved. If multiple threads of execution are involved, your current pattern of "if we can do this, then do it" breaks down, because it introduces race conditions. You need to assure that removing the item from inventory and paying for the item happen as a single, atomic, transaction. As long as you're sure it'll only have a single thread of execution involved though, your current method is safe.
In my Qt app I'd like to encode a pointer to an object as a string, pass it to another bit of code then decode it so that I can access the object.
This is part of internal drag and drop with a QTreeView. In my mimeData() method I have:
QMimeData * TreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData();
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
foreach (QModelIndex index, indexes)
{
QString colText;
if (index.isValid()) {
TreeItem *item = getItem(index);
// grab the text from each column
for(int cc=0; cc < item->columnCount(); cc++ ) {
colText = item->data(cc).toString();
stream << colText;
}
// add the pointer to the item
qDebug() << quint64(&item);
stream << quint64(&item);
}
}
mimeData->setData("application/vnd.text.list", encodedData);
return mimeData;
}
The qDebug() line produces a number like 140736277471632 which could be right, but is probably wrong.
How should I encode a pointer as a string so that it can be fed into a stream. And how should I then decode it and get the pointer to the original object?
Thank you.
I would dis-advice doing this.
Serializing objects in strings and de-serializing later makes sense for "moving" objects from one process to another. But within one process, you should pass pointers directly, or wrapped in a container like shared-pointer.
If the only way to pass something is a string, create an instance (e.g. QMap<QString, YourPointerType>) where you can register a pointer and access it by a string-name.
If you wrap this map in a class, you can check, if this pointer already exists while registering and if it still exists while retrieving.
Besides, in a models you can store anything you want using User-Roles. You are not limited to store your custom data as mime data.
Here you don't want to take the address of item, but its value. It's a pointer, its value is the address you're looking for, not its address (which, as already mentioned, is completely irrelevant and dangerous to manipulate once the if block scope is exited).
qDebug << qint64(&item);// will print the address this pointer is stored at.
qDebug << qint64(item);// will print the address this pointer is pointing at
EDIT: If you want to get the address back from a string into a pointer, read it as a number from a stringstream, i.e.:
std::istringstream is{str};
long pointer;//be careful with the size of a pointer in your case.
is >> pointer;
TreeItem* item = reinterpret_cast<TreeItem*>(q);
I've implemented a wxListCtrl and would like to sort the list alphabetically. Although this sounds similar as the question here, it is different with one major difference: I'm using SetItemData() and GetItemData() to store the index of a vector.
The code supplied here works nicely, but requires GetItemData(). Even though the comparison function does not refer to any of the item data, if I omit it, the sorting is not complete.
static int wxCALLBACK MyCompareFunction(long item1, long item2, long sortData) {
wxSortedListCtrl *ctrl = (wxSortedListCtrl*) sortData;
wxString a, b;
a = ctrl->GetItemText(item1, ctrl->GetSortedColumn());
b = ctrl->GetItemText(item2, ctrl->GetSortedColumn());
if( ctrl->IsSortedAscending() )
return a.CmpNoCase(b);
return b.CmpNoCase(a);
}
bool wxSortedListCtrl::SortItems(void) {
long item = -1;
for ( ;; ) {
item = GetNextItem(item);
if ( item == -1 )
break;
SetItemData(item, item);
//this is needed even though MyCompareFunction doesn't use it AT ALL.
//however it overwrites the data that I use myself...
}
return wxListCtrl::SortItems(MyCompareFunction, (long)this);
}
How would I use this sort function while maintaining the item data I've set myself with SetItemData()?
If you want to rely on the control to do the sorting (as opposed to resorting the items internally and just reinserting them into it in the correct order), you must be able to access the sort key (i.e. text in your case) via the item data. So, instead of just associating your own data with each item, associate some struct containing both the item text and your custom data with it.
Alternatively, use wxLC_VIRTUAL and just override OnGetItemXXX() to return the items in the correct order. This may sometimes be simpler than dealing with sorting the items manually and is definitely much more efficient for any non-trivial number of items.
I'm making an application that is querying a MySQL database.
I want the results of this to be stored in a map (which has a corresponding pair):
std::map<int, Car*> m_car;
typedef std::pair<int, Car*> m_car_pair;
The car object is made up of 8 parameters, one of which is car_id so firstly I pull the car ID and use it as the key then I want to store the entire car object as the value of the map. (I know this is casing me to be storing the car_id twice but for the moment I don't mind that).
Anyway here's my query code:
void DatabaseController::getAll(QString query_string)
{
// Console log message
std::cout << "Querying Database" << std::endl;
/// Declare SQL query and pass function parameter 'query'
QSqlQuery query(query_string);
// execute the query
query.exec();
int count = 0;
// While results are coming in
while(query.next())
{
// Call car constructor passing all parameters
Car car(query.value(count).toInt(), query.value(count+1).toString(), query.value(count+2).toString(), query.value(count+3).toString(),
query.value(count+4).toInt(), query.value(count+5).toInt(), query.value(count+6).toInt(), query.value(count+7).toString());
if (car.getcarID() != 0)
{
m_car_records.insert(m_car_pair(car.getcarID(), &car));
}
}
std::cout << "Database query finished" << std::endl;
After this I made a quick test function to iterate over the map and pull all of the ID's (map key) and check they were different (i.e. the function worked) and they were.
But that was just a check what I needed was to be able to call the accessory functions from car on the car objects that should be in the map. So I used the same quick test function to iterate over the map and cout << car.toString(); (a simple to string function in the car class):
void DatabaseController::test()
{
m_car_records_iterator = m_car_records.begin();
for(unsigned int i = 0; i < m_car_records.size(); i++)
{
car *test = m_car_records_iterator->second;
std::cout << test->toString() << std::endl;
m_car_records_iterator++;
}
}
This showed the correct number of results however they all were the same i.e. the car object that has been added to every entry in the map is the same (the values of the last record that was found by the query)
My Question is...
Is there any way that using this structure I currently have for my query I can create and add these class objects to my map within the while loop using the same name for each, because of course I can't know how many results are being returned and declare a class object for each one, but as it stands using the same name is just adding the same one every time not actually replacing the values... at least that's what I think is happening??
Any advice or idea would be welcomed (sorry for the long post)
You are experiencing undefined behavior. The reason is that you insert a pointer to a local variable in the map.
In the loop in getAll, when the loop starts over on the next item the car variable is not valid any more.
I suggest you look into std::shared_ptr for the pointers.
This is your problem--
Car car( ... ); // ***Stack allocated
if (car.getcarID() != 0)
{
m_car_records.insert(m_car_pair(car.getcarID(), &car));
}
//But the pointer is what's stored
When the loop iterates, the Car instance is destroyed and the pointer dangles, resulting in undefined behavior. You need
Car* car = new Car( ... );
and then when m_car is no longer needed, you need to iterate through and delete its Car values.
I need to get the number after the button to increment in a for loop. For example, button1 becomes button2, etc. I have tried appending a variable which increments but C++ Builder gives an error saying "Button is not a member of TMain." Is there any way to achieve the end goal or get around this?
You can't construct new identifiers from others at run time. The compiler is correct that Button really isn't a member of your TMain class.
Instead, build the string name of the component you want, and then call your form's FindComponent method to get the component with that name.
for (int i = 1; i <= 2; ++i) {
std::string name = "Button" + IntToStr(i);
TButton* button = dynamic_cast<TButton*>(this->FindComponent(name));
}
That requires that the buttons' Name properties be set accordingly.
Another solution is to forego the component names and put your objects in a proper container, like a vector. For example, you can override the Loaded method (which is where you can be sure all your form's components have been created) and fill a vector there:
void TMain::Loaded() {
TForm::Loaded();
this->m_buttons.push_back(Button1);
this->m_buttons.push_back(Button2);
}
Now when you want to iterate over your buttons, you just iterate over the vector instead:
for (std::vector<TButton*>::const_iterator it = m_buttons.begin();
it != m_buttons.end();
++it) {
// ...
}