I am creating a program that requires a form to be opened, and the previous to be closed on a push button click. My current problem is that when I click the button the new form is shown for a millisecond then disappears.
void mainMenu::on_mainLoginB_clicked()
{
logIn objlogIn;
objlogIn.show();
}
void mainMenu::on_mainExitB_clicked()
{
exit(1);
}
here is my header file
private slots:
void on_mainLoginB_clicked();
void on_mainExitB_clicked();
private:
Ui::mainMenu *ui;
};
objLogIn is declared in the scope of the SLOT, and for this reason, deleted when the function return.
Remember that QT, like most user interfaces works with an event (message) loop, so functions like show() do not block: they immediately return and it is the event loop managing it further.
For solving this issue:
Create the object in a higher scope and show it/hide it when required.
Create the object dynamically in the heap.
Related
I have a form (which I'll call MainForm) embedded with a TabControl. Every time the user creates a new tab it is filled with an instance of a pre-built Panel (which I'll call MyPanel) which contains many controls.
My MyPanel class has a private variable bool save_state which is set to false every time one of the (editable) controls is edited and set to true when the user "saves" the state of the panel.
I want a visual flag to keep track of tabs that have unsaved changes (e.g. the tab "Tab1" will instead display the text "Tab1 *" if it has unsaved changes). So I want to set up the event handler in my MainForm which can call a method in MyPanel to add the handler to each control.
Since not all my controls use the same EventHandler type (for example, I also need to track DataGridViewRowsAddedEvent, among others), I currently have several methods adding the appropriate handler to the corresponding controls (one for each type of Event Handler), each of which is running the same code (i.e. set the save_state bit to false and append " *" to the tab text.
For example, in MainForm.cpp I have:
#include "MyPanel.h"
void markUnsaved(void) {
// set panel bit to false
// append " *" to tab text if we haven't already
}
void MainForm::handler1(Object ^sender, EventArgs ^e) {
markUnsaved();
}
void MainForm::handler2(Object ^sender, DataGridViewRowsAddedEventArgs ^e) {
markUnsaved();
}
void Main::FormaddNewPanelToTab(int tab_index) {
// check index is valid ...
// make the new panel
MyPanel ^new_panel = gcnew MyPanel();
new_panel->addEventHandlerToControls(gcnew EventHandler(this, &MainForm::handler1));
new_panel->addDgvEventHandlerToControls(gcnew DataGridViewRowsAddedEventHandler(this, &MainForm::handler2));
// rest of code...
}
Though this currently works as intended, this (along with the several other Event Handler types I have to manage) makes my code look really silly.
I am hoping to be able to have have a single event handler in MainForm and a single method in MyPanel which type-casts the Event Handler passed and adds it to all the controls with the appropriate types.
I have tried doing simple casts such as:
void MyPanel::addHandlerToControls(EventHandler ^handler) {
control_NUD->ValueChanged += handler; // this works because ValueChanged is of type EventHandler
control_DGV->RowsAdded += (DataGridViewRowsAddedEventHandler ^)handler; // this compiles but throws an exception
// rest of the code...
}
to no avail.
Any help would be greatly appreciated!
I know this is maybe a bit late for answer but I'd want to show how would I solve this.
Firs of all I suggest to get rid from idea of casting event handlers. Kind of such approach may work in C# (with some adjustments) but as far as I know it's not possible in C++ /CLI.
I'd go for adding new event to a MyPanel class that will be invoked every time when the data on a panel is changed. But to avoid adding a lot of different handlers to a control events in a MyPanel class it's better to create one generic method that will handle all the neccessary control's events and fire new event. Maybe this sounds messy, let me show the code:
public ref class MyPanel
{
// Add a new event
public:
event EventHandler^ DataChanged;
// Add a method that will fire new event
// this methid will be invoked on every control's event that you'll subscribe
private:
generic <typename T>
void DataChangedHandler(System::Object^ sender, T e)
{
// Fire the event
DataChanged(this, EventArgs::Empty);
}
// Once the controls are initialized you may add the event handlers
// I put it in a constructor only for example
MyPanel()
{
control_NUD->ValueChanged += gcnew EventHandler(this, &MyPanel::DataChangedHandler<EventArgs^>);
control_DGV->RowsAdded += gcnew DataGridViewRowsAddedEventHandler(this, &MyPanel::DataChangedHandler<DataGridViewRowsAddedEventArgs^>);
// and so on...
}
}
/// And now in a main form we only need to subscribe to a DataChanged event
public ref class MainForm
{
//...
// the handler
void MyHandler(Object^ sender, EventArgs^ e)
{
markUnsaved();
}
void FormaddNewPanelToTab(int tab_index)
{
// make the new panel
MyPanel ^new_panel = gcnew MyPanel();
new_panel->DataChanged += gcnew EventHandler(this, &MainForm::MyHandler);
}
//...
}
Hope this helps.
I have a press button (pushButton_RenameTargets) and 3 labels (label_Tar1ex, label_Tar2ex, label_Tar3ex) on my main form with default text values. When I push the button (pushButton_RenameTargets) a dialog is created (renametargets). It has three text edit lines (lineEdit_Target1, lineEdit_Target2,lineEdit_Target3). When I enter names on the three text edit lines and push OK I want the 3 labels on my main form to update.
Better Described:
When the button is pressed:
void MainWindow::on_pushButton_RenameTargets_clicked()
{
RenameTargets renametargets;
renametargets.setModal(true);
renametargets.exec();
}
It creates the dialog window renametargets.
Window has three text edit lines (lineEdit_Target1, lineEdit_Target2,lineEdit_Target3).
When the OK button is pushed I store the text in QString variables.
void RenameTargets::on_buttonBox_TargetRename_accepted()
{
QString Target1NameInput = ui->lineEdit_Target1->text();
QString Target2NameInput = ui->lineEdit_Target2->text();
QString Target3NameInput = ui->lineEdit_Target3->text();
}
Questions:
(1) How can I set the text of QString Target1NameInput (located on second form: renametargets) to label_Tar1ex (located on main form) as I push the OK button on the dialog.
(2) How can I get to display label_Tar1ex (located on main form) to display on a label in the second form -- called label_CurrentName_Tar1ex.
Basically this is a renaming scheme.....
What I would do is declare Target1NameInput and others in your dialog's class instead of your Ok function. That way those variables always "exist" while your dialog exists. If you create them in your Ok function, then they vanish when that function ends, and then you can't get them from your mainWindow anymore.
Move the variable declarations to your dialog's class. (They go in public so other classes can get at em)
class RenameTargets : public QDialog
{
Q_OBJECT
public:
QString Target1NameInput; //Side note, variable naming convention says
QString Target2NameInput; //that variables should start with a lowercase
QString Target3NameInput; //letter, but totally up to you ;)
//Your other class stuff goes here
}
From that point you can set those variables in your dialog when Ok is pressed.
void RenameTargets::on_buttonBox_TargetRename_accepted()
{
Target1NameInput = ui->lineEdit_Target1->text();
Target2NameInput = ui->lineEdit_Target2->text();
Target3NameInput = ui->lineEdit_Target3->text();
}
And lastly, access those variables in your mainWindow.
void MainWindow::on_pushButton_RenameTargets_clicked()
{
RenameTargets renametargets;
renametargets.setModal(true);
if(renametargets.exec() == QDialog::Accepted) //Check if they clicked Ok
{
ui->label_Tar1ex->setText(renametargets.Target1NameInput);
ui->label_Tar2ex->setText(renametargets.Target2NameInput);
ui->label_Tar3ex->setText(renametargets.Target3NameInput);
}
}
As for your second question, sending from mainWindow to dialog, you have 2 options as I see it.
Set your string variables we created in your dialog class before exec().
Pass the text in your dialog constructor.
If option 1, then you simply call renametargets.Target1NameInput = ui->label_Tar1ex->text(); for each variable before you call renametargets.exec(); Then in your dialog's ui setup, you set your lineEdits text to those same variables.
Let me know if you want me to explain option 2 for you. ;)
There's also many other options to send variables between classes, this is just 1 of those ways. I believe the conventional thing to do would be to have get and set functions within your dialog class, but for my own personal projects, I find that overkill. Up to you.
if (editDocumentDialog->exec() == QDialog::Accepted)
{
editDocumentDialog->getDataRecord(theDocRecord);
documents->updateRecord(theDocRecord);
}
Why not using signal / slot?
void MainWindow::on_pushButton_RenameTargets_clicked()
{
RenameTargets renametargets;
connect(&renametargets, SIGNAL(name_inputted), this, SLOT(update_name_fields);
...
}
Then emit the signal in on_buttonBox_TargetRename_accepted, and update label_Tar1ex... in slot function. You may want to create RenameTargets in heap so it isn't destroyed immediately after its OK is clicked.
I wanted to do some action when a dialog shows when it opens or when it maximizes from a minimal status or it moves from out of a screen.
Does QT have such a signal?
I am also not sure where to find if QT has a list of signals defined.
Does each QT widget have a 'show' signal?
If you look at Qt source code then you will find QWidget::show to be a slot:
public Q_SLOTS:
// Widget management functions
virtual void setVisible(bool visible);
void setHidden(bool hidden);
void show();
The slot is mainly for us, programmers to make us able to connect with signals for specific purposes like clicking the button we created does something to certain widget. As for Windows or Mac OS, we have the app serving all the events coming from the system via event loop. And QWidget reacts on all the 'signals' in the form of system events coming and yes, may, execute show() or showMaximized() or showMinimized slots then.
But I can assume you want to overload
virtual void showEvent(QShowEvent *);
virtual void hideEvent(QHideEvent *);
Like:
void MyWidget::showEvent(QShowEvent *e)
{
if (isMaximized())
{
if (e->spontaneous())
{
// the author would like to know
// if the event is issued by the system
}
; // the action for maximized
}
else
{
; // the action for normal show
}
}
void MyWidget::hideEvent(QHideEvent *)
{
if (isMinimized())
{
; // the action for minimized
}
else
{
; // the action for hide
}
}
For recognizing cases when the system operates the widget we can use QEvent::spontaneous().
Please also refer to show and hide event doc pages:
http://doc.qt.io/qt-5/qshowevent-members.html
http://doc.qt.io/qt-5/qhideevent.html
I Set an int Variable for IDC_EDIT1 Control.
now i Want Change it With a Function, But when clicking on Button, Show an Error!
void test()
{
CthDlg d;
d.m_text1 = 5;
d.UpdateData(FALSE);
}
void CthDlg::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
// pThread = AfxBeginThread(ThreadFunction, THREAD_PRIORITY_NORMAL);
test();
}
In the test function you define a completely new instance of the CthDlg class, and try to modify it. That will not work as it's not properly created, and also have no relation with the actual dialog being displayed.
Instead, if test is a stand-alone (not member) function then you should pass the actual dialog instance as an argument, and use that.
For example
void tesy(CthDlg& dlg)
{
dlg.m_text1 = ...;
dlg.UpdateData(FALSE);
}
void CthDlg::OnBnClickedOk()
{
test(*this);
}
The controls are created when you call DoModal or Create.
And therefore calling UpdateData will only succeed when the Dialog is created.
This is the usual sequence: The value members may be set before you Launch the Control. The data is transferred when the Dialog is created and transfered back from the controls into the data members when the Dialog is closed with OnOK.
I have an RPC thread that is calling back to me from that thread. I need to somehow inform Qt that it needs to make a function call from the main thread. In straight Windows I could do this by using a custom message and then posting that message to the message queue, e.g., I could create a WM_CALLFUNCTION message and pass the function pointer through wParam and the parameter (class pointer) through lParam.
Has anyone an idea how I could do this with Qt? I've come across QCustomEvent but I have no idea how to use it or how to process it. Any help would be hugely appreciated!
Edit:
In the end I went with QMetaObject::invokeMethod which works perfectly.
Using custom events generally involves creating your own QEvent subclass, overriding customEvent() in the QObject class that will receive the event (often the main window class) and some code that "posts" the event from your thread to the receiver.
I like to implement the event posting code as a method of the receiver class. That way, the caller only has to know about the recevier object and not any of the "Qt" specifics. The caller will invoke this method which will then essentially post a message to itself. Hopefully the code below will make it clearer.
// MainWindow.h
...
// Define your custom event identifier
const QEvent::Type MY_CUSTOM_EVENT = static_cast<QEvent::Type>(QEvent::User + 1);
// Define your custom event subclass
class MyCustomEvent : public QEvent
{
public:
MyCustomEvent(const int customData1, const int customData2):
QEvent(MY_CUSTOM_EVENT),
m_customData1(customData1),
m_customData2(customData2)
{
}
int getCustomData1() const
{
return m_customData1;
}
int getCustomData2() const
{
return m_customData2;
}
private:
int m_customData1;
int m_customData2;
};
public:
void postMyCustomEvent(const int customData1, const int customData2);
....
protected:
void customEvent(QEvent *event); // This overrides QObject::customEvent()
...
private:
void handleMyCustomEvent(const MyCustomEvent *event);
The customData1 and customData2 are there to demonstrate how you might pass some data along in your event. They don't have to be ints.
// MainWindow.cpp
...
void MainWindow::postMyCustomEvent(const int customData1, const int customData2)
{
// This method (postMyCustomEvent) can be called from any thread
QApplication::postEvent(this, new MyCustomEvent(customData1, customData2));
}
void MainWindow::customEvent(QEvent * event)
{
// When we get here, we've crossed the thread boundary and are now
// executing in the Qt object's thread
if(event->type() == MY_CUSTOM_EVENT)
{
handleMyCustomEvent(static_cast<MyCustomEvent *>(event));
}
// use more else ifs to handle other custom events
}
void MainWindow::handleMyCustomEvent(const MyCustomEvent *event)
{
// Now you can safely do something with your Qt objects.
// Access your custom data using event->getCustomData1() etc.
}
I hope I didn't leave anything out. With this in place, code in some other thread just needs to get a pointer to a MainWindow object (let's call it mainWindow) and call
mainWindow->postMyCustomEvent(1,2);
where, just for our example, 1 and 2 can be any integer data.
In Qt 3, the usual way to communicate
with the GUI thread from a non-GUI
thread was by posting a custom event
to a QObject in the GUI thread. In Qt
4, this still works and can be
generalized to the case where one
thread needs to communicate with any
other thread that has an event loop.
To ease programming, Qt 4 also allows
you to establish signal--slot
connections across threads. Behind the
scenes, these connections are
implemented using an event. If the
signal has any parameters, these are
also stored in the event. Like
previously, if the sender and receiver
live in the same thread, Qt makes a
direct function call.
--
http://doc.qt.nokia.com/qq/qq14-threading.html#signalslotconnectionsacrossthreads