So far I have written some simple code for a wxWidgets application, like creating a menu, frame and a few buttons. To follow the process of exiting, I have this function that shows a message box :
int OnExit( )
{
wxMessageBox( "Closing the application", wxOK | wxICON_INFORMATION )
return 0;
}
Closing the application by clicking close ( X ) button shows the message box and then exits. But closing it by clicking the "Quit" menu item doesn't work for me. I have tried copying some code from an old example and from CodeBlocks basic sample code that comes with wxWidgets project, with no luck. Please show me a method of closing the application from the menu item.
Try searching the web for "wxwidgets close window menu":
wxWidgets Hello World Example
In your OnExit function you need to call the Close method as in the example.
// Build: g++ this.cpp -std=gnu++11 $(wx-config --cxxflags --libs core,base)
#include <wx/wx.h>
class CApp : public wxApp
{
public:
bool OnInit() {
// Create the main frame.
wxFrame * frame = new wxFrame(NULL, wxID_ANY, wxT("demo"));
// Add the menubar
wxMenu * menus[] = {new wxMenu, new wxMenu};
wxString labels[] = {wxT("&File"), wxT("&Help")};
frame->wxFrame::SetMenuBar(new wxMenuBar(2, menus, labels));
menus[0]->Append(wxID_EXIT);
// Bind an event handling method for menu item wxID_EXIT.
this->Bind(wxEVT_MENU, [frame](wxCommandEvent &)->void{
frame->Close();
/* 1. method wxWindow::Close
* 2. event type wxEVT_CLOSE_WINDOW
* 3. method wxTopLevelWindow::OnCloseWindow
* 4. method wxTopLevelWindow::Destroy (overriding wxWindow::Destroy)
* 5. op delete
*/
}, wxID_EXIT);
// Enter the message loop.
frame->Centre(wxBOTH);
frame->Show(true);
return true;
}
int OnExit() {
wxMessageBox("Closing the application", wxEmptyString, wxOK | wxICON_INFORMATION);
return this->wxApp::OnExit();
}
};
wxDECLARE_APP(CApp);
wxIMPLEMENT_APP(CApp);
Related
I want to destroy a modeless wxDialog when the window is closed.
What I tried is:
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
class MyDialog : public wxDialog {
public:
MyDialog() : wxDialog(nullptr, wxID_ANY, "Dialog", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
// Comment next 2 lines out for it to work as expected.
auto* sizer = CreateButtonSizer(wxOK);
this->SetSizerAndFit(sizer);
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &) { this->Destroy(); });
}
};
class MyApp : public wxApp {
public:
auto OnInit() -> bool override {
if (!wxApp::OnInit()) {
return false;
}
auto *dialog = new MyDialog();
dialog->Show(true);
return true;
}
};
wxIMPLEMENT_APP(MyApp);
But for some reason the application does not exit.
Edit, from the comments thanks to #catalin:
wxVersion: v3.1.5
Deleting everything in the ctor except the Bind exits the app as expected.
But just having something simple as a new wxButton(this, wxID_OK, "OK") in the ctor prevents the app from exiting.
If the ctor is empty the app also doesn't exit when closing the dialog.
Edit2:
Pressing the button or "ESC" does not exit the app. Pressing the "X" in top right (on Windows) closes it, but shouldn't the behaviour be the same according to the documentation:
EVT_CLOSE(func): The dialog is being closed by the user or programmatically (see wxWindow::Close). The user may generate this event clicking the close button (typically the 'X' on the top-right of the title bar) if it's present (see the wxCLOSE_BOX style) or by clicking a button with the wxID_CANCEL or wxID_OK ids.
Last Edit (I hope):
I somewhat solved it. Adding Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {this->Destroy();}); closes the app on button click, the Bind for wxEVT_CLOSE_WINDOW closes it on "X" click. But still the wxOK Button should generate a wxEVT_CLOSE_WINDOW not a wxEVT_BUTTON event according to doc.
I want to show a menu in Qt with the following functionality: clicking anywhere outside of the menu will close it, but the click will be processed by the receiving widget. As far as I know, this rules out using setWindowFlag(Qt::Popup) because the click that closed the popup will be 'eaten' and not processed any further. I'd love to know if there is a way around this.
For the time being, I'm overriding focusOutEvent like so:
class Menu : public QWidget
{
Q_OBJECT
public:
Menu(QWidget* parent = nullptr)
: QWidget(parent){
setWindowFlags(Qt::FramelessWindowHint);
resize(200, 100);
}
public slots:
void showMenu(QPoint point)
{
move(point);
show();
setFocus(); // so that focusOutEvent will be trigerred
}
protected:
void focusOutEvent(QFocusEvent *event) override
{
hide();
}
};
And I'm using it like this:
class Window : public QMainWindow
{
Q_OBJECT
public:
Window(): m_menu()
{
auto* list = new QListWidget();
list->addItems(QStringList() << "item1" << "item2" << "item3");
connect(list, &QListWidget::itemClicked, [this](){
m_menu.showMenu(QCursor::pos());
});
// Layout stuff
auto central_widget = new QWidget();
auto layout = new QVBoxLayout(central_widget);
layout->addWidget(list);
layout->addItem(new QSpacerItem(20,200, QSizePolicy::Minimum, QSizePolicy::Expanding));
setCentralWidget(central_widget);
}
public:
~Window(){}
private:
Menu m_menu;
};
Single clicking on any of the items in the list will show the menu and clicking anywhere on the grey background will close the menu.
The issue I'm facing is that when the menu is shown, focus is lost from the main window (photo on the right, the titlebar buttons and selected item are greyed out). Focus is lost because of calling setFocus on the menu but if I don't use it then the QFocusOutEvents are not trigerred and the menu doesn't close.
The same happens if I use setAttribute(Qt::WA_ShowWithoutActivating) on the menu.
So here are my questions:
Is there a way to use Qt::Popup without the 'eaten' clicks issue?
If not, is there a way to show the menu without losing the focus from the main window but still trigger the focusOutEvents as necessary?
Using Qt 5.15.1 on MacOS 11.2.3. Thanks for reading through my long post!
Here is the piece of code that gives me a problem :
void CMainFrame::DisplayActionsPopupMenu()
{
// get "Actions" menu
wxMenuBar* pMenuBar = GetMenuBar();
ASSERT(pMenuBar != NULL);
int nIndex = pMenuBar->FindMenu("Actions");
ASSERT(nIndex != wxNOT_FOUND);
wxMenu *pMenuActions = pMenuBar->GetMenu(nIndex);
ASSERT(pMenuActions != NULL);
// display a popup menu for actions
PopupMenu(pMenuActions);
}
What I try to do here is to display a popupmenu when right clicking and I want it to be the same as the second menu in the menubar of my project.
It worked when I compiled with wxWidgets v2.8
Now I tried with v3.0 and here is the error:
../src/common/menucmn.cpp(715): assert "!IsAttached()" failed in SetInvokingWindow(): menus attached to menu bar can't have invoking window
What should I do to fix this?
I think a more robust solution than the existing answer consisting in detaching and attaching back the menu would be to just create a new menu instead, e.g. something like this:
std::unique_ptr<wxMenu> CreateActionsMenu() { ... }
// In your frame ctor or wherever you initialize your menu bar.
MyFrame::MyFrame() {
wxMenuBar* const mb = new wxMenuBar;
mb->Append(CreateActionsMenu().release(), "&Actions");
SetMenuBar(mb);
}
// In your event handler function showing the popup menu.
void MyFrame::OnShowPopup(wxCommandEvent&) {
auto menu = CreateActionsMenu();
PopupMenu(menu.get());
}
Creating a menu is relatively fast and there should be no problem doing it just before showing it (although, of course, you could also cache it for later if it's really huge or otherwise expensive to construct).
Finally I found that with the >3.0 wxWidgets version, you can't get elements from the wxMenuBar which is attached to your frame. So you have to temporarly unattach and reattach it.
Here is how you would so:
1 - Initialize the new wxMenu with the MenuBar. In my case:
wxMenuBar* pMenuBar = GetMenuBar();
ASSERT(pMenuBar != NULL);
cout<<pMenuBar->IsAttached()<<endl;
int nIndex = pMenuBar->FindMenu("Actions");
ASSERT(nIndex != wxNOT_FOUND);
wxMenu *pMenuActions = pMenuBar->GetMenu(nIndex);
2 - Check if it's attached:
if(pMenuActions->IsAttached()){
pMenuActions->Detach();
}
3 - When your done, reAttach the wxMenu to the wxMenuBar
pMenuActions->Attach(pMenuBar);
I open QDialog window from QMainWindow. Now when I press the QDialog window
its not always closing in the first press - I need to press few times (3-4) to close it .
I have closeEvent slot that has simple event->accept(); inside it.
This is how I call the QDialog from the main window:
void MyManager::DialogContainerOpen(type t)
{
if(pMyDialogContainer == NULL)
{
pMyDialogContainer = new MyDialogContainer();
}
int returnVal = QDialog::Rejected;
if(!m_bContainer)
{
m_bContainer = true;
int returnVal = pMyDialogContainer->exec();
if(returnVal != QDialog::Accepted ) {
m_bContainer = false;
}
}
}
This is the first problem.
The second problem is how do i set the QDialog windows NOT to be allays on top? (I don’t want it to block the parent window.
UPDATE
well i found out that the function from the MainWindow that showing the contexMenu
and inside it has the connect single/slot is keeps to invoke so i just used the disconnect
i dont know if its the best sulotion but its working.
now i juat have the final problem .
here is the code i hope its ok
void MainWindowContainer::ShowContextMenu(const QPoint& pos) // this is a slot
{
QModelIndex modelIndx;
QPoint globalPos = ui.treeView_mainwindow->mapToGlobal(pos);
bool b1 = connect(OpenAction, SIGNAL(triggered()),m_SignalMapper, SLOT(map()) );
m_SignalMapper->setMapping(OpenAction,voidID);
bool b2 = connect(m_SignalMapper, SIGNAL(mapped(QString)), this, SLOT(OpenWin(QString)));
QAction* selectedItem = ContextMenu.exec(globalPos);
}
void MainWindowContainer::OpenWin(QString gid)
{
//disconnect(sender0, SIGNAL(overflow()),receiver1, SLOT(handleMathError()));
disconnect(m_SignalMapper, SIGNAL(mapped(QString)),this, SLOT(OpenWin(QString)));
disconnect(OpenAction,SIGNAL(triggered()),m_SignalMapper, SLOT(map()));
....
....
}
For your second question, the term you are looking for is modal vs modeless dialogs. The QDialog documentation tells exactly how you create non-modal dialogs:
Modeless dialogs are displayed using show(), which returns control to the caller immediately.
i.e. don't use exec() as that will make a modal dialog (which blocks the parent).
You should not connect the same signal/slot more than once unless you want the action run multiple times. All you need to do is to connect the QAction's signal to the slot once. This is usually done in the constructor (or a dedicated function called from the constructor) where you create the action.
Similar problem to last time. I run the code, exit it. The GUI disappears and it appears to have been fully exited but I check my process list and the code is there still holding memory.
I've narrowed it down to a call to wxFileDialog. I don't understand what I'm doing wrong because I'm calling much in a similar way as I'm calling a wxDialog. The wxDialog call doesn't result in this problem but the wxFileDialog does cause it.
Works fine:
// Help Dialog
void MyFrame::OnHelp(wxCommandEvent& WXUNUSED(event))
{
wxBoxSizer *topsizer;
wxHtmlWindow *html;
wxDialog dlg(this, wxID_ANY, wxString(_("Help")));
topsizer = new wxBoxSizer(wxVERTICAL);
html = new wxHtmlWindow(&dlg, wxID_ANY, wxDefaultPosition,
wxSize(380, 160), wxHW_SCROLLBAR_NEVER);
html->SetBorders(0);
html->LoadPage(wxT("data/help.html"));
// Fit the HTML window to the size of its contents
html->SetSize(html->GetInternalRepresentation()->GetWidth(),
html->GetInternalRepresentation()->GetHeight());
topsizer->Add(html, 1, wxALL, 10);
wxButton *but = new wxButton(&dlg, wxID_OK, _("OK"));
but->SetDefault();
topsizer->Add(but, 0, wxALL | wxALIGN_RIGHT, 15);
dlg.SetSizer(topsizer);
topsizer->Fit(&dlg);
dlg.CentreOnParent();
dlg.ShowModal();
}
Causes problem:
void MyFrame::OnVidFile(wxCommandEvent& WXUNUSED(event))
{
wxString caption = wxT("Choose a file");
wxString wildcard = wxT("AVI files (*.avi)|*.avi");
wxString defaultDir = wxGetHomeDir();
wxString defaultFilename = wxEmptyString;
wxFileDialog dialog(this, caption, defaultDir, defaultFilename, wildcard, wxOPEN | wxFILE_MUST_EXIST );
dialog.CentreOnParent();
if(dialog.ShowModal() == wxID_OK) // problem
pathVid = dialog.GetPath().c_str();
}
This results in the program not terminating correctly even if I throw in a dialog.Destroy(); or dialog.Close(); at the end. They look like similar calls to the dialog class to me?
Am I missing something here?
Problem is a conflict with OpenCV (specifically HighGUI).
To avoid this problem and the OLE error problem, override the WxWidgets initialize base class virtual as follows:
bool MywxApp::Initialize( int& argc, wxChar **argv )
{
::CoUninitialize();
return wxApp::Initialize( argc, argv );
}