Drag and drop issue in Qt: Pass parameter to receiving dropEvent - c++

A healthPackButton is dropped on a mysquare. Now I would like to add a value to this button (because I have a number of healthPackButtons and I want to be able to differentiate them). I have tried changing the makeDrag function for it to accept an extra parameter but than my SIGNAL was no longer matched.
Question: How can I pass additional information (=> int value) to my dropEvent handler inside another class.
Dialog class
for (int i=0; i<healthPks.size(); i++){
int value = healthPks.at(i);
QPushButton *healthPackButton = new QPushButton(title,this);
connect(healthPackButton,SIGNAL(pressed()),this,SLOT(makeDrag()));
}
void Dialog::makeDrag(){
QDrag *drag = new QDrag(this);
QMimeData *mime = new QMimeData;
mime->setText("This is a test");
drag->setMimeData(mime);
drag->start();
}
mysquare class
void MySquare::dropEvent(QGraphicsSceneDragDropEvent *event){
isHealthPack=true;
int xCoord = curX/width;
int yCoord = curY/height;
int value = 0; //what's the value??
const QMimeData *mimeData = event->mimeData();
emit healthMapChanged(xCoord,yCoord,value);
update();
}

To get an additional parameter into your slot, you can use a QSignalMapper – the documentation has an example of adding a QString const& parameter, but you can use an int in exactly the same way to pass the value to a makeDrag(int).
You could then use QMimeType's setData (converting your int to a QByteArray using QByteArray::number for example) to get that value to the drop target.

Related

QT how to emit a signal in this case

I need to develop a Sudoku game. After reading a text file containing the number values, I create 9*9 widgets.
If (the value is already set then I instantiate to a qlabel containing the number,
else I instantiate a combobox containing the possible values of each case).
Until here everything is OK.
The problem is that when a value is chosen from the combobox, I need to draw it in a square in my view (MVC). The problem is how can I know which one was chosen?
The only signal can I use from combobox signals is currenttextchanged(QString), but I'll not know which combo made that signal.
The ideal for me would be something like this SIGNAL(curretextchanged(QString, int, int)), but I don't know if I can define a new signal?
Here is some code:
QWidget *tab[9][9];
SudModel *modele = ???;
QComboBox *combobox = new QComboBox();
combobox->setStyleSheet("border: 1px solid red");
int tmp = modele->valuesof(i, j).size();
for (int s = 0; s < tmp; s++) {
combobox->addItem(QString::number(modele->valuesof(i, j)[s]));
}
connect(combobox, SIGNAL(currentTextChanged(QString)), this, SLOT(update()));
tab[i][j] = combobox;
One solution would be to attach that extra information when you connect to the signal for a specific combobox.
For example, say you have your function update(QString text, int x, int y) then you could attach the signal to a lambda that calls the function with the extra arguments, captured at connect time. Something like this:
connect(combobox, &QComboBox::currentTextChanged, [x, y, this](const QString& text){ this->update(text, x, y); });
That would then call the update functions with the x and y values captured when the connection was made along with the text argument that originated from the signal.
You can ask the slot for a sender()
QObject *QObject::sender() const
Returns a pointer to the object that sent the signal, if called in a
slot activated by a signal; otherwise it returns 0. The pointer is
valid only during the execution of the slot that calls this
function from this object's thread context.
You can use QSignalMapper.
In this you map every item with mapper with any specific string. Now when any mapped item emits signal you can know which item emitted it by the string.
You can use the same with minor changes like using Signal 'currentTextChanged' as mentioned. But the same result can be obtained by using 'currentIndexChanged' signal with some modifications.
signalMapper = new QSignalMapper(this);
for (int i = 0; i < 2; ++i) {
QComboBox *combo = new QComboBox();
connect(combo, SIGNAL(currentIndexChanged()), signalMapper, SLOT(map()));
signalMapper->setMapping(combo, "combo" + i);
}
connect(signalMapper, SIGNAL(mapped(QString)),
this, SIGNAL(indexChanged(QString)));
NOTE : (source Qt Assistant)
QSignalMapper class collects a set of parameterless signals, and
re-emits them with integer, string or widget parameters corresponding
to the object that sent the signal

Do an action while any checkbox state is modified with Qt

In my programm, I fill my interface with a lot of checkbox by this way :
void VGCCC::addMaterialToUI(QDomNodeList _materialNodeList, QWidget* _areaWidget, QLayout* _layout, QWidget* _layoutWidget, int _maTable)
{
for(int i=0; i< _materialNodeList.count();i++)
{
QDomElement materialElement = _materialNodeList.at(i).toElement();
QString elementFile = materialElement.attribute("file");
QString elementId = materialElement.attribute("id");
QString elementLabel = elementId;
elementLabel += " - ";
elementLabel += materialElement.attribute("label");
QCheckBox* checkbox = new QCheckBox(elementLabel);
_layout->addWidget(checkbox);
_layoutWidget->adjustSize();
_areaWidget->setMinimumHeight(_layoutWidget->height());
_areaWidget->setMinimumWidth(_layoutWidget->width());
configuration c;
c.path = (m_igmPath+elementFile).toStdString();
c.id = elementId.toInt();
c.name = elementLabel.toStdString();
if(_maTable==0)
{
m_materialSectionMap[checkbox] = c;
}
else
{
m_materialPostMap[checkbox] = c;
}
}
}
I would like to know how to retrieve these "abstract" checkbox. More exactly, if one of these checkbox is checked, I would like to call another function like this :
connect(anyCheckbox,SIGNAL(stateChanged(anyCheckbox)), this, SLOT(doSomethingFunctionIfCheckboxIsChecked()));
The difficulty is that in my UI, these checkbox didn't exist, so I can't connect them to my function. How can I solve it ?
You can e.g. collect pointers to your checkbox objects to a list so can access or "retrieve" them later.
You can connect each checkbox's stateChanged signal to a same slot which is then called when state of any of the checkboxes is changed. In the slot you can cast the sender() to a checkbox if you need to know which specific checkbox is in question. Another alternative is to use QSignalMapper.
In your class declaration:
private slots:
void checkboxStateChanged(int state)
private:
QList<QCheckBox*> m_checkboxes;
In your class definition:
void VGCCC::addMaterialToUI(QDomNodeList _materialNodeList, QWidget* _areaWidget, QLayout* _layout, QWidget* _layoutWidget, int _maTable)
{
...
QCheckBox* checkbox = new QCheckBox(elementLabel);
m_checkboxes.append(checkbox);
connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(checkboxStateChanged(int)));
...
}
void VGCCC::checkboxStateChanged(int state)
{
// Here your can e.g. call doSomethingFunctionIfCheckboxIsChecked()
QCheckBox* checkbox = qobject_cast<QCheckBox*>(sender());
if (checkbox)
{
// checkbox points to the object whose state changed
}
}

QT - Can I dynamically point to GUI's labels using a variable passed into a method?

I'm creating an inventory and want to reference my inventory Forms QLabels with the name of the item passed into my method.
The QLabels and the names of the items are the same so I wanted to reduce if statements by referencing a QLabel depending on the name of the item passed in.
void Zork::on_btnTake_clicked()
{
Item *item = new Item(ui->takeItem->currentText().toStdString());
Colin->addItemToInventory(item);
inventory_screen->addItem(item->getDescription()); //Enables the Item in the Inventory
currentRoom->deleteItem(item);
ui->takeItem->removeItem(ui->takeItem->currentIndex()); //Only remove the item in the foreground of combobox at the time take button was clicked
}
Calls this Method addItemToInventory(item):
void Inventory_Ui:: addItem(string itemName){
myUi->itemName->setText(QString::fromStdString(itemName));
}
I am unable to do so as itemName is not a member of Ui, although itemName does contain members of Ui.
Example:
In my Inventory_Ui Form I have 6 labels, one named broken_bottle. I want to pass broken_bottle into the method's parameter itemName and them use that as a reference so at run-time it would look like myUi->broken_bottle->setText...
I think I follow what you are asking to do. I would access it with findChild or findChildren.
http://doc.qt.io/qt-5/qobject.html#findChild
http://doc.qt.io/qt-5/qobject.html#findChildren
QList <QLabel *> labels = ui->myForm()->findChildren<QLabel*>();
QLabel* targetLabel = 0;
foreach(QLabel * label, labels)
{
if(label->text() == ui->takeItem->currentText())
{
targetLabel = label;
break;
}
}
if(targetLabel != 0)
{
// ... do something with targetLabel
}
or...
QList <QLabel *> labels = ui->myForm()->findChildren<QLabel*>();
foreach(QLabel * label, labels)
label->setObjectName(label->text());
// later
QLabel * targetLabel = ui->myForm()->findChild<QLabel*>("text of label");
if(targetLabel)
// use it
Or if you are connecting all these labels to the same slot, you can figure some of it out using the sender static method.
http://doc.qt.io/qt-5/qobject.html#sender
void MainWindow::on_takeItem_currentTextChanged()
{
QLabel* targetLabel = qobject_cast<QLabel*>QObject::sender();
if(targetLabel)
// do something with it
}
Hope that helps.

Extract menu action data in receiving function or slot

In my menu, I am setting data to the menu actions. How can I extract that data in my slot? Or even better, instead of connecting a slot, can I also connect a member function that is able to extract the action data (like in the 1st connect)? The action data is meant to identify each action. As a sidenode, I am not sure if I can use several menu action entries on only one openNote-action.
void Traymenu::createMainContextMenu() {
QAction *actionNewNote = m_mainContextMenu.addAction("Neue Notiz");
actionNewNote->setIcon(QIcon("C:\\new.ico"));
actionNewNote->setIconVisibleInMenu(true);
QObject::connect(actionNewNote,&QAction::triggered,this,&Traymenu::newNote);
QString menuEntryName;
QAction *openNote;
QVariant noteID;
for (int i = 0; i<m_noteList.count(); i++) {
std::string noteTitle = m_noteList[i].data()->getTitle();
menuEntryName = QString::fromStdString(noteTitle);
openNote = m_mainContextMenu.addAction(menuEntryName);
connect(openNote,SIGNAL(triggered()),this,SLOT(s_showNote()));
noteID.setValue(m_noteList[i].data()->getID());
openNote->setData(noteID);
}
m_mainIcon.setContextMenu(&m_mainContextMenu);
}
And the slot:
void Traymenu::s_showNote() {
QObject* obj = sender();
//int noteID = data.toInt();
//Search all notes in noteList for that ID and show it
}
Using QObject::sender()
You can use QObject::sender() to get the signal's sender, followed by qobject_cast to cast the sender pointer to the right type.
void Traymenu::s_showNote()
{
QAction* act = qobject_cast<QAction *>(sender());
if (act != 0)
{
QVariant data = act->data();
int noteID = data.toInt();
showNote(noteID); // isolate showNote logic from "get my ID" stuff
}
}
void Traymenu::showNote(int noteID)
{
// Do the real work here, now that you have the ID ...
}
As the Qt documentation warns, "This function violates the object-oriented principle of modularity." It's still a fairly safe and standard practice, though — just one with some shortcomings. In particular, note that you're committing to having a s_showNote method that only works when it's accessed as a slot (otherwise sender is 0).
Using QSignalMapper
Alternatively, you can use the QSignalMapper class to return a pointer to teh item or to associate a unique identifier (int or QString) with each item.
Something like this:
void Traymenu::createMainContextMenu()
{
signalMapper = new QSignalMapper(this); // (or initialize elsewhere)
// ... (create your newNote here same as before) ...
QString menuEntryName;
QAction *openNote;
int noteID;
for (int i = 0; i<m_noteList.count(); i++) {
std::string noteTitle = m_noteList[i].data()->getTitle();
menuEntryName = QString::fromStdString(noteTitle);
openNote = m_mainContextMenu.addAction(menuEntryName);
noteID = m_noteList[i].data()->getID();
openNote->setData(QVariant(noteID)); // (if you still need data in the QActions)
signalMapper->setMapping(openNote, noteID);
}
connect(signalMapper, SIGNAL(mapped(int)),
this, SLOT(showNote(int)));
m_mainIcon.setContextMenu(&m_mainContextMenu);
}
void Traymenu::showNote(int noteID) {
// Now you have the ID ...
}
This pattern has the benefit of isolating all the ugly "Wait, how do I get my identifier?" stuff in one spot, instead of having both the initialization code and the slot function having code for associating actions and IDs.
I would write it like:
void Traymenu::s_showNote() {
QObject* obj = sender();
QAction *action = qobject_cast<QAction *>(obj);
int id = action->data().toInt();
for (int i = 0; i < m_noteList.count(); i++) {
if (m_noteList[i].data()->getID() == id) {
[..]
}
}
}

convert qtablewidget item to float

I Have a Qtablewidget , and a signal cellchanged(), that calls a Slot Change();
setup_ui->tableWidget->setRowCount(1);
setup_ui->tableWidget->setColumnCount(2);
QTableWidgetItem *item = new QTableWidgetItem("Gain");
setup_ui->tableWidget->setHorizontalHeaderItem(0,item);
QTableWidgetItem *item1 = new QTableWidgetItem("Offset");
setup_ui->tableWidget->setHorizontalHeaderItem(1,item1);
However , i couldn't convert the cell edited value to float, something like
setup_ui->tableWidget->item(0,0)->float();
Please help,
what you need within the slot
Class::Change( int row, int column ) {
bool ok
float value = setup_ui->tableWidget->item(row, column)->text().toFloat(&ok);
if (ok) {
// do whatever with value
}
else {
// do some error handling
}
}
You can skip the flag (bool ok) if you can guarantee that the conversion never fails!
For more info, post more code ...