PDA

View Full Version : Reopen Form Widget after deletion



Fivezero05
11th August 2016, 11:00
I want to open a new form, close it an reopen it.

However, when I reopen it, all changed stuff still is there (like text in a label or something like that). Now I am not really concerned with these trivial visual ... leftovers, but more with the inner working. Will a timer keep running after i close this window? Keep updating labels with pictures? (I don't really want that by default)

So what I want is : I open a new form, after that form is closed, it will be destroyed an renewed. This would result in a fresh screen ready to start.

I know it is not that crazy. In VB.NET it looks like this:

// the initialization :


Private WithEvents _frmLogging As frmLogging

//opening the form, by pressing a button or something


_frmLogging = New frmLogging
_frmLogging.Show()

//deleting it when it is closed :


Private Sub CatchClosingLog(sender As Object, e As EventArgs) Handles _frmLogging.FormClosed
_frmLogging.Dispose()
_frmLogging = Nothing
End Sub

I want to replicate this in Qt . frmMain is my Qmainwindow and I want to open frmTest which is represented by oTestFrame.


void frmMain::on_btnOpenNew_clicked()
{
oTestFrame.show();
__hook(&frmTest::FormClosed, &oTestFrame, &frmMain::OnCloseSecondTry); //Here I create an event. This allows me to run code in frmMain after frmTest is closed
}

When oTestFrame is closed, OnCloseSecondTry is executed. That is were it all goes wrong.


void frmMain::OnCloseSecondTry()
{
__unhook(&frmTest::FormClosed,& oTestFrame, &frmMain::OnCloseSecondTry); //unhook event, just good practice, I don't really think it will matter after the object is destroyed (Am I wrong?)
oTestFrame.~frmTest();
frmTest oTemp;
oTestFrame = &oTemp;
}

The important variables are declared in frmMain.h :


...
private:
frmTest oTestFrame;
void OnCloseSecondTry();
...

I destroy oTestFrame. But I can not insert a fresh frmTest object in oTestFrame.
I read somewhere you could not store a Qwidget in a Qwidget. Is that true?
How can i get this to work? Any suggestions welcome :P

anda_skoa
11th August 2016, 13:17
You can just set the Qt::WA_DeleteOnClose widget attribute, then it will be deleted when it is closed.

Cheers,
_

d_stranz
11th August 2016, 16:06
I read somewhere you could not store a Qwidget in a Qwidget. Is that true?

Don't believe everything you read, especially if you read it "somewhere"...

The whole architecture of building complex GUIs in Qt is based on storing QWidget pointers as members (and children) of other QWidgets. However, these are pointers. QWidgets are not copyable or assignable; that is, you cannot write code like:



QWidget a;
QWidget b;

a = b;

QWidget c = a;

// etc.


You can write code that manipulates QWidget pointers or references:



QWidget * a = new QWidget();
QWidget * b = new QWidget();

a = b; // a and b both point to the same QWidget instance; the previous b instance is lost and is a memory leak...

QWidget * c = a; // c now points to the same QWidget instance as a and b


You can do this, but it isn't generally recommended in most cases:



class MyWidget : public QWidget
{
Q_OBJECT;

// ...

private:
SomeOtherWidget a;
};


The main reason why this isn't recommended is that having widgets (not widget pointers) as members of other widgets can interfere with the parent-child ownership relationships inherent in Qt. If you initialize "a" with a pointer to "this" (i.e. the MyWidget instance being constructed), then when the MyWidget instance goes out of scope, you could end up in a double destruction situation - "a" gets destroyed because it is a member variable of MyWidget, but the MyWidget instance also tries to destroy it because it is its child. One of those will lose. The only way to do it successfully is to construct "a" with no parent.

When I would use something like this is if "SomeOtherWidget" is a QDialog or other complex widget that is expensive to construct or if I want its contents to persist from invocation to invocation. Then I can just re-use the instance each time I need to post the dialog, and it will retain all of the settings that it had when it was last invoked. If I want a fresh dialog with a default initialization each time, then I don't use this method, I create the dialog as a stack variable in the function that invokes it.

Fivezero05
12th August 2016, 08:34
Thanks to d_stranz (http://www.qtcentre.org/members/5428-d_stranz) and anda_skoa (http://www.qtcentre.org/members/305-anda_skoa), I was able to solve my problem :P Here is how i did it.

frmMain represents the Qmainwindow.
frmTest is the extra window I want to open and close. The variable for this is oTestFrame.

frmMain.h


...
private:
frmTest * oTestFrame ; //how frmTest should be defined. This is a pointer.
private slots:
void onClosed();
...

frmMain.cpp


frmMain::frmMain(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);

oTestFrame = new frmTest(); //for the first time, we initialize the object in the frmMain constructor. Now we can check if a form already exists or not
}

void frmMain::on_btnOpenNew_clicked() //this can be anything. For this case, a button was used.
{
if (!oTestFrame->isActiveWindow()) //only one window can exist at all time.
{
oTestFrame->show();
QObject::connect(oTestFrame, SIGNAL(destroyed()), this, SLOT(onClosed()));
}
}

void frmMain::onClosed()
{
QObject::disconnect(oTestFrame, SIGNAL(destroyed()), this, SLOT(onClosed())); //I don't really know for sure if this is necessary, after destruction, but hey, you never know
oTestFrame = new frmTest(); //ready a new form te be opened. All previous changes are lost, which was the point.

}


frmTest.cpp : not a lot extra code is needed to let this work


frmTest::frmTest(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
this->setAttribute(Qt::WA_DeleteOnClose, true); // This makes sure the form is destroyed after it is closed.
}

anda_skoa
12th August 2016, 10:48
I would change that to connect() right after creating the object.

And there is no need to disconnect, the object is gone afterwards.

Neither of which is even necessary if you use a QPointer to track the object and only create one when you need it.



private:
QPointer<frmTest> oTestFrame;




void frmMain::on_btnOpenNew_clicked()
{
if (!oTestFrame) {
oTestFrame = new frmTest();
oTestFrame->show();
}
}


Cheers,
_