Why does a Method only work in my constructor?
-
Hello all,
First year university student here, so I may be making a silly mistake. But let me explain my issue. So currently, I am trying to use a stacked widget, along with a QVector of MainWindows, to allow the user to sift through each stacked widget index and interact with a plot on the current index. Relevant code snippets are provided.
MainWindow.h: QStackedWidget *s = new QStackedWidget(); TestMainWindow *currentPageWindow = new TestMainWindow(); MainWindow.cpp: //QVECTOR FOR ANOTHER CLASS - TESTMAINWINDOW QVector< TestMainWindow * > pages; // CONSTRUCTOR MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { TestMainWindow *a = new TestMainWindow(); TestMainWindow *b = new TestMainWindow(); pages.append(a); pages.append(b); s->addWidget(a); s->addWidget(b); setCurrentPage(a); setCurrentPage(b); //setCurrentPage(a); // I tested these two lines interchangeably to make sure the window would change to only allow me to operate on the current index. it worked here setCentralWidget(s); } //METHOD IN QUESTION void MainWindow::createNewPageWindow(){ TestMainWindow *t = new TestMainWindow(); pages.append(t); s->addWidget(t); setCurrentPage(t); } // SLOTS void MainWindow::previousPage() { s->setCurrentIndex((s->currentIndex()) - 1); setCurrentPage(pages[s->currentIndex()]); } void MainWindow::nextPage() { s->setCurrentIndex((s->currentIndex()) + 1); setCurrentPage(pages[s->currentIndex()]); } void MainWindow::setCurrentPage(TestMainWindow *t){ currentPageWindow = t; }
In debugging and conducting tests, I've found the the slots/signals I've used with these methods work fine. As mentioned in the title, the setCurrentPage method works fine in the constructor. If I were to uncomment the setCurrentPage(a) line above, it would work as intended. However, when I call the method in other functions, it does not work.
The TestMainWindow is another class I've developed, and it works as intended. I've added the slot just in case something is off, but I am 99% sure it has to do with one of my functions in the MainWindow class. Here it is:
connect(addPageAction, &QAction::triggered, currentPageWindow, &TestMainWindow::addPage);
In reviewing this code, could someone help point me in the right direction? Thank you in advance!
-
@WesLow
The problem with your method/variable is that it is too fragile at attempting to correctly keep in sync with what the stacked widget's current page/index actually is. For example, if anything changess->currentIndex()
by whatever means you will not update your variable accordingly. If you really wanted to maintain a variable, a more robust approach would be to look atQStackedWidget
's Signals: put a slot oncurrentChanged(int index)
(maybewidgetRemoved(int index)
too) and update your variable there. At least that cannot then get "out of sync".But it is still easier to sub-class and add the two methods I showed earlier.
-
@WesLow said in Why does a Method only work in my constructor?:
However, when I call the method in other functions, it does not work.
Show just what you are calling in functions other than the constructor. At present variables
a
&b
are local to the constructor, you won't be able to use them elsewhere, so how are trying to access the desired page forsetCurrentPage()
in other methods?I don't follow how your
setCurrentPage()
settingcurrentPageWindow = t
is supposed to work. It's obviously something to do withconnect(addPageAction, &QAction::triggered, currentPageWindow, &TestMainWindow::addPage);
, but I don't know when/where you do thatconnect()
.Basically, you only want to a given
connect()
once, and that should be immediately after the signalling or slot object is created.One thing occurs to me which maybe you are assuming would work but will not. When you go
connect(addPageAction, &QAction::triggered, currentPageWindow, &TestMainWindow::addPage);
if you think that is a "dynamic" connection, so that it will connect to (for the slot object) whatever is going to be the
currentPageWindow
at any future time, it does not work like that. In that statement it sets a connection to a slot for the objectcurrentPageWindow
at the time theconnect()
statement is executed, only for that object-value ofcurrentPageWindow
. WhencurrentPageWindow
changes at runtime, theconnect()
does not "change with it", it remains as connecting to what it did atconnect()
time, if you see what I mean. -
@WesLow
To add to @JonB
Your whole "set-the-current-page-and-have-the-current-page-as-private-member" - thing is pretty weird :)
(->currentPageWindow
member and your "function in question"createNewPageWindow(...)
+setCurrentPage(...)
)
There is no need to do that. If you need the current/activeTestMainWindow
widget-page, you can simply get it by usingQStackedWidget
'scurrentWidget()
function.
This will return your page asQWidget
. You can cast it toTestMainWindow
afterwards (dont forget aboutnullptr
and valid checks!). -
Thank you both for the responses. I had initially tried the setCurrentWidget method to no avail, but will try it again.
How would I successfully make a StackedWidget (and all its connected components) global?
-
@WesLow said in Why does a Method only work in my constructor?:
How would I successfully make a StackedWidget (and all its connected components) global?
You wouldn't/shouldn't! What do you want to make "global" and why? The fact that there are pages in a
QStackedWidget
should not have a direct connection to globality. -
Okay, thank you! I was wanting to make the QStackedWidget global at the time. I probably should have explained this better but when I call my slots/methods in the constructor it works fine; they just won't work when I call them in functions outside the constructor.
What code snippets would be the most beneficial to you? Sorry if I didn't explain well enough.
-
@WesLow said in Why does a Method only work in my constructor?:
but when I call my slots/methods in the constructor it works fine; they just won't work when I call them in functions outside the constructor.
Well, a bit of code which you say works in the constructor, and a bit of code where you say it doesn't work not in the constructor? For my old brain at least, the minimal amount of code the better --- cut out all lines which have nothing to do with the problem. I can't imagine a problem that takes more than 20 lines to illustrate!
-
Constructor
// CONSTRUCTOR MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setCentralWidget(s); /* Test Code to ensure setCurrentPage works in Constructor TestMainWindow *a = new TestMainWindow(); TestMainWindow *b = new TestMainWindow(); pages.append(a); pages.append(b); s->addWidget(a); s->addWidget(b); setCurrentPage(a); setCurrentPage(b); setCurrentPage(a); */ }
Works in Constructor but not Outside It:
void MainWindow::setCurrentPage(TestMainWindow *t){ currentPageWindow = t; }
Works in and outside of Constructor (Everything but Calling setCurrentPage):
void MainWindow::createNewPageWindow(){ TestMainWindow *t = new TestMainWindow(); pages.append(t); s->addWidget(t); setCurrentPage(t); } void MainWindow::previousPage() { s->setCurrentIndex((s->currentIndex()) - 1); setCurrentPage(pages[s->currentIndex()]); } void MainWindow::nextPage() { s->setCurrentIndex((s->currentIndex()) + 1); setCurrentPage(pages[s->currentIndex()]); }
-
Also declared these in my header file for reference:
QStackedWidget *s = new QStackedWidget(); TestMainWindow *currentPageWindow = new TestMainWindow();
-
@WesLow
So far as I can see, this is much the same as the code has always been, so I don't know if you are showing something significant. You keep saying things like:Works in Constructor but not Outside It:
Works in and outside of Constructor (Everything but Calling setCurrentPage):
WHAT "does not work"? WHAT about "Calling setCurrentPage" does/does not work? Do you get a compilation error? Do you get a runtime error? Does something not behave in the way you expect it to? What is supposed to happen which does not happen? Do you have some
qDebug()
statements in your code showing what is going wrong? What is the actual question? I am not a mind-reader. -
My apologies for the lack of clarification. Thank you for your patience.
I do not get any compilation errors; instead I get logical errors (it does not behave in the way I expect it to).
What is supposed to happen, and happens as intended:
When I click the next/previous icons on my toolbar (left arrow for previous page, right arrow for next page), I want the QStackedWidget to move to the next or previous index of the stacked widget s. This works as intended without any issues.When I clicked on the status menu option to create a new TestMainWindow widget (the test main window widget include multiple dock widgets with plotting features - the TestMainWindow class has worked as intended for a while), it works as intended, creating a new TestMainWindow object and adding it to the end of the stacked widget.
What is supposed to happen, and doesn't happen as intended:
When the next/previous toolbar icons are clicked, I want the current Widget displayed by StackWidget s to be able to be used/edited by the user (i.e. new plots added/other functions from the TestMainWindowClass called). In other words, I want the user to be able to call functions from the TestMainWindow class on the currentWidget (on the currentIndex) of s.This is where the main problem stems from: I am not able to set the current widget of s to be the widget that is displayed to the user. This essentially cause the widget to get "hung" on one index, even if the currentindex is changed. When compiling the app with the constructor code uncommented, for example, you could edit the first index (containing widget a), but you could not edit widget b, even if you changed the index).
The same thing happens when the create NewPageWindow method; the TestMainWindow object is created and added to the stack widget, but it does not set the current widget to the object as intended.
Once again, let me know if I need to clarify better. I am very grateful for your patience and understanding.
-
@WesLow
Yournext
/previousPage
slots seem to correctly inc/decrement the current page index, and set yourcurrentPageWindow
to the page. Have you actually connected the toolbar icon slots correctly? Why don't you putqDebug() << s->currentIndex()
statements into them so you know what is going on?I see your code setting your own
currentPageWindow
variable, but since it never reads/tests it there is no relevance. So I don't know what you expect from that variable. -
So after doing some debugging/testing I've found that it has to do with:
QVector< TestMainWindow * > pages;
When I call
setCurrentPage(pages[s->currentIndex()]); // Note: I also tried calling setCurrentPage(pages[1]) just to see if I was able to set the current index to the correct object; the problem is when I try to use this vector it does not give me the object store in the vector.
Or try to get the TestMainWindow object stored in the vector, it does not give me it. Thus, I will try to find a way to get the TestMainWindow object stored in the index of the vector equal to the current index of the stacked widget. Any ideas on what I could do here? Thank you in advance!
-
Quick Update:
I changed the QVector to a regular c++ vector and now the issues spawning specifically from the QVector are resolved.However...
The remaining issue one hundred percent has something to do with the setCurrentPageMethod. Simply put, it performs as expected if called in the constructor, but does not perform as expected if called outside the constructor. Going to pick back up in the morning.
Also, as far as your original explanation on the "dynamic" point. So essentially, even if the address of the variable changes, it will still connect to the original address?
-
@WesLow
Look, I don't know what's wrong with your code, there are various possibilities.Why don't you cut your losses as @Pl45m4 suggested? In some shape or form, you are trying to "track" what the current widget in a
QStackedLayout
is viasetCurrentPage()
setting yourcurrentPageWindow
member variable. The problem is that can --- and perhaps apparently does --- get "out of sync" with what is actually the stack's current index/widget.QStackedWidget
already has a current index (currentIndex()
) and a current widget (currentWidget()
). Both of those also have setters. So you can useQWidget *QStackedWidget::currentWidget() const
to read what is current andvoid QStackedWidget::setCurrentWidget(QWidget *widget)
to set what is current.Get rid of your own
currentPageWindow
member variable and use these instead. If you want them to be typed for your case of the widgets always beingTestMainWindow
, sub-class fromQStackedWidget
and add your own helper methods:const TestMainWindow *currentPage() { return qobject_cast<TestMainWindow *>(currentWidget()); }; void setCurrentPage(TestMainWindow *page) { setCurrentWidget(page); };
Now you do not need your variable at all, use these and nothing will get out of sync.
-
@Pl45m4 said in Why does a Method only work in my constructor?:
QStackedWidget's currentWidget() function.
This will return your page as QWidget. You can cast it to TestMainWindow afterwards (dont forget about nullptr and valid checks!).Just got it going! Although I'm not proud of how long it took, I just reach the desired output. Glad I decided to stay up. Thank you for your help, the bit about it not being able to be dynamic really helped point me in the right direction after I fully grasped what you were saying. Thank you both!
-
Also I apologize if I seemed hard-headed at times.
-
@JonB But for better coding practices I will probably implement your solution; I overcomplicated things heavily.
-
@WesLow
The problem with your method/variable is that it is too fragile at attempting to correctly keep in sync with what the stacked widget's current page/index actually is. For example, if anything changess->currentIndex()
by whatever means you will not update your variable accordingly. If you really wanted to maintain a variable, a more robust approach would be to look atQStackedWidget
's Signals: put a slot oncurrentChanged(int index)
(maybewidgetRemoved(int index)
too) and update your variable there. At least that cannot then get "out of sync".But it is still easier to sub-class and add the two methods I showed earlier.