Application crashes when opening a window qt - c++

I would like to create a program that shows a question with some answers to the user. My application uses 3 forms: Main Menu, Login Menu and Game Form, and all inherit from an abstract class called Form; I do this because it allows the use of the factory method, which it create a new window when a signal GoFw is emitted by the actual form.
The "loop" that shows the windows is the following: MainMenu -> LoginMenu -> GameForm -> MainMenu...
The problem is when the game is finished (e.g. the count of remaining questions is zero) the GameForm emits the signal GoFw but the application crashes after the show() method (I could see a white window with no buttons before the crash).
The debugger show a messagebox with this error:
The inferior stopped because it triggered an exception.
Stopped in thread 0 by: Exception at 0x723f7b93, code: 0xc0000005: read access
violation at: 0x0, flags=0x0 (first chance).
and QtCreator opens a file called: Disassembler(QHash::findNode)
This is the code of the factory method:
void FormFactory::Init(int formType)
{
///if formType == 1 MainMenu is showed
if(formType == MAIN_MENU_TYPE)
{
//inizializza il puntatore
actualForm = new MainMenu();
}
///else if is == 2 show ProfileMenu
else if(formType == PROFILE_MENU_TYPE)
{
actualForm = new LoginMenu();
}
///else if == 3 show GameForm
else if(formType == GAME_FORM_TYPE)
{
actualForm = new GameForm();
}
///else if there is no match launch an exception
else
throw UnexpectedIdEx();
connect(actualForm, SIGNAL(GoFw(int)), this, SLOT(DisplayForm(int)));
}
void FormFactory::DisplayForm(int i)
{
Reset();
Init(i);
///show the form pointed by actualform
actualForm->show();
}
void FormFactory::Reset()
{
disconnect(actualForm, SIGNAL(GoFw(int)), this, SLOT(DisplayForm(int)));
///if actualform is actually pointing to a form, delete it and set actualForm to zero
if(actualForm!=0)
delete actualForm;
actualForm = 0;
}
And the code of MainMenu.cpp is
MainMenu::MainMenu()
{
setUpGui();
}
void MainMenu::setUpGui()
{
playButton = new QPushButton(tr("Play"));
infoButton = new QPushButton(tr("Info"));
quitButton = new QPushButton(tr("Exit"));
///connect the clicked signal to the related slot
connect(infoButton, SIGNAL(clicked()), this, SLOT(info()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(quit()));
connect(playButton, SIGNAL(clicked()), this, SLOT(emitSig()));
///create the vertical layout
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(playButton);
layout->addWidget(infoButton);
layout->addWidget(quitButton);
setLayout(layout);
setWindowTitle(tr("Menu Principale"));
}
void MainMenu::emitSig()
{
emit GoFw(2);
}
Thank you all for your help,
Luca

I'd suggest rethink your solution, it seems you overcomplicated it with the factory method.
Just use 3 variables for the forms, do the "new" operation once for each, and use show() / hide() methods depending on your signals.
To answer to the crash problem, one reason i see is because you do "delete" in the slot.
From Qt doc:
Warning: Deleting a QObject while pending events are waiting to be delivered can cause a crash. You must not delete the QObject directly if it exists in a different thread than the one currently executing. Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it.

Related

slow response on right click contex menu in graphic scene Qt

I have set a large menu in event filter on right click with 45-50 actions
inside and I find that when I right click the response to show the menu is slow
I did try the same code with 5 actions in the menu and the response was fine.
Is there something wrong with this way of coding on a contex menu ?
eventFilter
bool Editor::eventFilter(QObject *o, QEvent *e)
{
Q_UNUSED (o);
QGraphicsSceneMouseEvent *me = (QGraphicsSceneMouseEvent*) e;
switch ((int) e->type()){
case QEvent::GraphicsSceneMousePress:{
switch ((int) me->button()){
case Qt::RightButton:{
QGraphicsItem *item = itemAt(me->scenePos());
showContextMenu(item->scenePos().toPoint());
return true;
}
//more cases here//
}
break;
}
}
return QObject::eventFilter(o, e);
}
showContextMenu
void Editor::showContextMenu(const QPoint &pos)
{
QGraphicsItem *item =itemAt(pos);
// Create main effe menu
effeMenu= new QMenu("Menu");
QString menuStyle(
"QMenu {"
"border:10px };"
//more code here
);
effeMenu->setStyleSheet(menuStyle);
AmpMenu=effeMenu->addMenu(QIcon(":/effectImg/img/effePng/amp.png"),"Amp");
Amp1 =AmpMenu->addAction(QIcon(":/effectImg/img/effePng/amp.png"),"Amp 1");
Amp2 =AmpMenu->addAction(QIcon(":/effectImg/img/effePng/amp.png"),"Amp 2");
CabMenu=effeMenu->addMenu(QIcon(":/effectImg/img/effePng/cab.png"),"Cab");
Cab1 =CabMenu->addAction(QIcon(":/effectImg/img/effePng/cab.png"),"Cab 1");
Cab2 =CabMenu->addAction(QIcon(":/effectImg/img/effePng/cab.png"),"Cab 2");
.
.
.
.
//45 actions more
connect(effeMenu, &QMenu::triggered,this,[this,&item](QAction * k){
menuSelection(k,item);
});
Instead of creating a new QMenu each time you call showContextMenu you could make it a member of the class and build it once. On the other hand it is not necessary to use a signal, you could simply use the exec() method of QMenu:
*.h
class Editor: ...{
...
private:
QMenu effeMenu;
}
*.cpp
Editor::Editor(...){
effeMenu.setTitle("Menu");
QString menuStyle(
"QMenu {"
"border:10px };"
//more code here
);
effeMenu.setStyleSheet(menuStyle);
AmpMenu=effeMenu.addMenu(QIcon(":/effectImg/img/effePng/amp.png"),"Amp");
Amp1 =AmpMenu->addAction(QIcon(":/effectImg/img/effePng/amp.png"),"Amp 1");
Amp2 =AmpMenu->addAction(QIcon(":/effectImg/img/effePng/amp.png"),"Amp 2");
CabMenu=effeMenu.addMenu(QIcon(":/effectImg/img/effePng/cab.png"),"Cab");
Cab1 =CabMenu->addAction(QIcon(":/effectImg/img/effePng/cab.png"),"Cab 1");
Cab2 =CabMenu->addAction(QIcon(":/effectImg/img/effePng/cab.png"),"Cab 2");
...
}
void Editor::showContextMenu(const QPoint &pos){
QGraphicsItem *item =itemAt(pos);
QAction *action = menu.exec(pos);
menuSelection(action, item);
}
There are two things you can do to improve speed:
1 - itemAt(pos) is costly, and you are doing it twice, one in the event, and one in the showContextMenu. From what I could understand from your code you don't need the item in the event, just in the showMenu.
2 - The menu creation that you are doing is expensive: all the actions have pixmaps. this allocs memory for the QPixmap, loads, execute, dumps. Because you told us that you use around 40 actions (and really, that's too much for a menu), this can get costly.
My advice:
Create a class for your menu, create one instance of it, add a setter for the current QGraphicsObject that your menu will work on, and always use that one instance.

Deleting a widget from QTableView

I have a delete button(QPushButton) in the last column of each row of my table view. I am creating these push buttons and directly setting them in view. Since I have allocated memory dynamically I wish to free this memory but I haven't stored pointers of these buttons anywhere so I am trying to obtain the widget at the time of clean up and deleting them.
SDelegate* myDelegate;
myDelegate = new SDelegate();
STableModel* model = new STableModel(1, 7, this);
myWindow->tableView->setModel(model);
myWindow->tableView->setItemDelegate(myDelegate);
for(int i = 0; i < no_of_rows; ++i) {
QPushButton* deleteButton = new QPushButton();
myWindow->tableView->setIndexWidget(model->index(i, 6), deleteButton);
}
exec();
// Cleanup
for(int i = 0; i < no_of_rows; ++i) {
// code works fine on removing this particular section
QWidget* widget = myWindow->tableView->indexWidget(model->index(i, 6));
if (widget)
delete widget;
}
delete model;
delete myDelegate;
I am getting a crash in qt5cored.dll (Unhandled exception) and application is crashing in qcoreapplication.h at the following code:
#ifndef QT_NO_QOBJECT
inline bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{ if (event) event->spont = false; return self ? self->notifyInternal(receiver, event) : false; }
While debugging there is no issue in deleting these widgets but code crashes afterwards at some other point. I am using QTableView and custom class for model which has inherited QAbstractTableModel.
There's a Qt bug that manifests as follow: if there are any index widgets, and you invoke setModel(nullptr) on the view, it'll crash in an assertion on visualRow != -1 in qtableview.cpp:1625 (in Qt 5.6.0). Presumably this bug could be triggered when the model is being removed in some other fashion too.
But I can't reproduce it by merely destroying the model instance. So I doubt that it's relevant here unless you get the same assertion failure.
Given the style of your code, it's more likely that you have a memory bug elsewhere. If you think that the code above is crashing, you should have a self-contained test case that demonstrates the crash. Is your model or delegate to blame? Would it crash using no delegate? Would it crash using a stock model?
Your code excerpt seems to be fine, if mostly unnecessary. You could allocate the delegate and the model locally. The buttons are owned by the view: as soon as the need for the buttons goes away, such as when the model changes the row count or goes away, they will get appropriately deleted. So you don't have to delete them yourself, it's safe but completely unnecessary.
Here's an example that demonstrates that in all cases, the buttons will get disposed when the model gets destroyed or the view gets destroyed, whichever comes first. Tracking object lifetime is super simple in Qt: keep a set of objects, and remove them from the set using a functor attached to the object's destroyed signal. In Qt 4 you'd use a helper class with a slot.
// https://github.com/KubaO/stackoverflown/tree/master/questions/model-indexwidget-del-38796375
#include <QtWidgets>
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QSet<QObject*> live;
{
QDialog dialog;
QVBoxLayout layout{&dialog};
QTableView view;
QPushButton clear{"Clear"};
layout.addWidget(&view);
layout.addWidget(&clear);
QScopedPointer<QStringListModel> model{new QStringListModel{&dialog}};
model->setStringList(QStringList{"a", "b", "c"});
view.setModel(model.data());
for (int i = 0; i < model->rowCount(); ++i) {
auto deleteButton = new QPushButton;
view.setIndexWidget(model->index(i), deleteButton);
live.insert(deleteButton);
QObject::connect(deleteButton, &QObject::destroyed, [&](QObject* obj) {
live.remove(obj); });
}
QObject::connect(&clear, &QPushButton::clicked, [&]{ model.reset(); });
dialog.exec();
Q_ASSERT(model || live.isEmpty());
}
Q_ASSERT(live.isEmpty());
}
Check out QObject::deleteLater() , it usually helps with issues around deleting QObjects / QWidgets.

The inferior stopped because it received signal from the Operating System error on resizable scrollarea

I have a QScrollArea inside QTabWidget and I have a QWidget beside my QTabWidget. I want QScrollArea to be resized when my main window is resized, so I have made this code like this:
void frmSummaryContact::on_btnAddNewContact_clicked()
{
MainWindow *mnWindow = qobject_cast<MainWindow *>(this->parent()->parent()->parent()->parent()->parent()->parent());
QTabWidget *tbWidget = qobject_cast<QTabWidget *>(this->parent()->parent()->parent()->parent());
frmDetailContact *frm = new frmDetailContact(mnWindow, "input", -1, mnWindow->rightPane());
QScrollArea *scrlForm = new QScrollArea;
scrlForm->setWidgetResizable(true);
scrlForm->setWidget(frm);
mnWindow->AddNewTab(tbWidget, scrlForm, "Add Contact");
}
my QTabWidget is in different form, so I cast it with qobject_cast. Meanwhile in another form, I have a toogle button to hide QWidget so my QTabWidget get wider. So in that form I have a code like this:
void frmDetailContactToggle::on_btnSearch_clicked()
{
MainWindow *mnWindow = qobject_cast<MainWindow *>(this->parent()->parent()->parent());
QLayoutItem *child;
while ((child = mnWindow->rightPane()->layout()->takeAt(0)) != 0)
child->widget()->setVisible(false);
mnWindow->rightPane()->setVisible(false);
QScrollArea *scrlContent = qobject_cast<QScrollArea *>(mnWindow->tabContentWidget()->currentWidget());
scrlContent->setWidgetResizable(false);
mnWindow->tabContentWidget()->setGeometry(mnWindow->tabContentWidget()->x(), mnWindow->tabContentWidget()->y(), m_width - mnWindow->tabContentWidget()->x() - 10, mnWindow->tabContentWidget()->height());
scrlContent->setWidgetResizable(true);
m_showRightPane = false;
}
I have realized that I can't change the geometry when WidgetResizable is true. It showed "The inferior stopped because it received signal from the Operating System" error. So I thought about making it false, changing the geometry, and making it true again. But when I want to make it true, I encounter the same error. Could anyone please help me to solve my problem?
if your program used uninitialized pointers,that may causes SIGSEGV.

qt, signal slots not connecting?

I have a qdialog, with a buttonbox at the bottom; Why isn't this slot not getting fired when a "signal" occurs? The code look like the following:
std::unique_ptr<MW::GenStd> box(new MW::GenStd(&tOut, &tIn));
box->ui.ChoiceButtons->addButton ("Ask",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("OverWrite",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("merge",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("Skip",
QDialogButtonBox::RejectRole );
QObject::connect(box->ui.ChoiceButtons, SIGNAL(clicked(QPushButton* b)), box.get(), SLOT(OnClick(QPushButton* b)));
return box->exec();
Where MW::GenStd is a dialog box (and ui.ChoicButtons a buttonbox). The modal dialog is correctly displayed - however it doesn't seem to interact at all.. Pressing the buttons doesn't fire the event. The slot is declared like the following:
public slots:
void OnClick(QPushButton* b) {
auto s(b->text());
if (s == "Merge") {
setResult(2);
} else if (s == "Overwrite") {
setResult(1);
} else if (s == "Skip") {
setResult(0);
} else if (s == "Ask") {
setResult(3);
}
}
};
(I know it's terribly to do such a string comparison here, but it's just as a quick mock up test to validate the buttons). But debugging shows the function isn't ever reached!
EDIT: as suggested looking at the output showed a culprit:
Object::connect: No such signal QDialogButtonBox::clicked(QPushButton*) in AskGUISupport.cpp:150
However that seems totally strange as the QDialogButtonBox does have a clicked signal? documentation
Do not use variable names in connect:
QObject::connect(box->ui.ChoiceButtons, SIGNAL(clicked(QPushButton*)),
box.get(), SLOT(OnClick(QPushButton*)));
QDialogButtonBox has a signal clicked ( QAbstractButton * button ) so you need to define a slot void OnClick(QAbstractButton* b) and connect to it. Use QAbstractButton, not QPushButton.
QDialogButtonBox class does not has signal
clicked(QPushButton*).
It has clicked ( QAbstractButton*) insted.
You should be very precise in signatures, when using signals/slots mechanisms. Any casts not allowed, because Qt uses strings internally to check signatures.
You should use clicked (QAbstractButton*) signature and adjust your slot to accpet QAbstractButton*. Make a slot
void OnClick(QAbstractButton* b);

Qt, PushButton, id attribute? Any way to know which button was clicked

void MainWindow::addRadioToUI()
{ int button_cunter=4;
while(!database.isEmpty())
{ button_cunter++;
QPushButton *one = new QPushButton("Play: "+name(get_r.getTrackId()));
one->setIcon(QIcon(":/images/play_button.png"));
one->setMaximumWidth(140);
one->setFlat(true);
QGroupBox* get_rGB = new QGroupBox("somethink");
QFormLayout* layout = new QFormLayout;
if(button_cunter%5 == 0){
layout->addWidget(one);
}
get_rGB->setLayout(layout);
scrollAreaWidgetContents->layout()->addWidget(get_rGB);
}
}
I have a few QPushButtons which are added automaticlly.
Is there a way to add "id attribute or sth else" to button and next know which button was clicked? I have different action for each button.
QApplication offers sender() which contains which object sent the signal. So you can do:
//slot, this could also be done in a switch
if(button[X] == QApplication::sender()){
doX();
}else if(button[Y] == QApplication::sender()){
doY();
}
http://doc.qt.io/qt-4.8/qobject.html#sender
QSignalMapper is pretty good for this type of thing.
You would define your slot like this for instance:
public slots:
void clicked(int buttonId); // or maybe trackId
Then add a QSignalMapper* member to your class and connect it to that slot:
signalMapper = new QSignalMapper(this);
connect(signalMapper, SIGNAL(mapped(int)),
this, SLOT(clicked(int)));
In the addRadioToUI, after creating your push button, do:
signalMapper.setMapping(one, button_cunter);
// or trackId if that's more practical
If all you need is a pointer to the object that triggered the signal though, you can use the static QOjbect::sender function in your slot to get a handle to that.
Use QButtonGroup. It takes id as a parameter when a button is added and provides the id to a slot when a button in the group is pressed.