How to destroy wxWidgets Popup ONLY on clicking button inside it? - c++

I have created a popup using "wxPopupTransientWindow" and added some text and a button. Now I want to destroy this popup only on Clicking this particular button. Right now, Popup will get destroyed if click anywhere outside it.
Here is what I have tried so far.
SimpleTransientPopup::SimpleTransientPopup(wxWindow *parent, bool scrolled) : wxPopupTransientWindow(parent, wxFRAME_SHAPED)
{
m_bGotItClick = false;
m_panel = new wxScrolledWindow(this,-1,wxDefaultPosition,wxDefaultSize);
m_panel->SetBackgroundColour(*wxYELLOW);
wxStaticText *text = new wxStaticText( m_panel, wxID_ANY,
"This panel will guide you stepwise through the workflow\n");
wxPanel *pBottomPanel = new wxPanel(m_panel, -1);
m_button = new wxButton(pBottomPanel, Minimal_PopupButton, "Got it!",wxDefaultPosition, wxDefaultSize);
m_link = new wxHyperlinkCtrl(pBottomPanel, Minimal_PopupLink, _("Hide these tips"), _(""), wxDefaultPosition, wxDefaultSize, wxHL_ALIGN_LEFT);
m_link->SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL, true));
m_link->SetNormalColour(*wxLIGHT_GREY);
m_link->SetForegroundColour(*wxLIGHT_GREY);
wxBoxSizer *topSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer *bottomSizer = new wxBoxSizer(wxHORIZONTAL);
bottomSizer->Add(m_link,0,wxALL,20);
bottomSizer->AddStretchSpacer();
bottomSizer->Add(m_button, 0, wxALL, 20);
pBottomPanel->SetAutoLayout(true);
pBottomPanel->SetSizer(bottomSizer);
bottomSizer->SetSizeHints(pBottomPanel);
bottomSizer->Fit(pBottomPanel);
topSizer->Add(text, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 20);
topSizer->Add(pBottomPanel,0,wxEXPAND);
m_panel->SetSizer( topSizer );
// Use the fitting size for the panel if we don't need scrollbars.
topSizer->Fit(m_panel);
SetClientSize(m_panel->GetSize());
wxTipKind tipKind = wxTipKind_Auto;
const int offsetY = SetTipShapeAndSize(tipKind, GetBestSize());
if (offsetY > 0)
{
// Offset our contents by the tip height to make it appear in the
// main rectangle.
topSizer->PrependSpacer(offsetY);
}
m_panel->Fit();
}
I am calling this OnDismiss function on clicking the button.
void SimpleTransientPopup::OnDismiss()
{
if (m_bGotItClick) {
wxLogMessage("%p SimpleTransientPopup::OnDismiss", this);
wxPopupTransientWindow::OnDismiss();
m_bGotItClick = false;
}
}
PS: I am new to wxWidgets

By overriding "wxPopupTransientWindow::Dismiss()" I was able to close the popup when button is clicked.
void SimpleTransientPopup::Dismiss()
{
if (m_bGotItClick) {
wxLogMessage("%p SimpleTransientPopup::Dismiss", this);
wxPopupTransientWindow::Dismiss();
m_bGotItClick = false;
}
}

Related

wxWidgets - setting an image as background

I am trying to set an image as the background but I have no idea how to do it.
It has been hours. The first lines of code I copied from somebody and they seemed alright but it still doesn't work.
The error that I have appears in a separate window and says it cannot load/find the image.
#include "MainFrame.h"
#include <wx/wx.h>
#include "FrameTwo.h"
#include "wx/custombgwin.h"
#include <wx/dcclient.h>
enum IDs {
BUTTON_ID=2
};
wxBEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_BUTTON(BUTTON_ID, MainFrame::OnLoginClicked)
wxEND_EVENT_TABLE()
MainFrame::MainFrame(const wxString& title) : wxFrame(nullptr, wxID_ANY, title) {
wxPanel* panel = new wxPanel(this);
wxClientDC dc(panel);
wxMemoryDC mdc;
int w, h;
w = 600;
h = 600;
dc.GetSize(&w, &h);
wxImage img(wxT("C:\\Users\\ALEX\\Desktop\\background_uno.jpeg"), wxBITMAP_TYPE_JPEG);
wxBitmap cat(img.Scale(w, h, wxIMAGE_QUALITY_HIGH));
mdc.SelectObject(cat);
dc.Blit(0, 0, cat.GetWidth(), cat.GetHeight(), &mdc, 0, 0, wxCOPY, 0);
mdc.SelectObject(wxNullBitmap);
//panel->SetBackgroundColour(wxColour(255, 102, 102)); //light orange-red
//panel->SetBackgroundColour(wxColour(155, 202, 62)); //fresh green
wxButton* login = new wxButton(panel, BUTTON_ID, "Log in", wxPoint(340, 350), wxSize(100, 35));
CreateStatusBar(); //creates a bar in the bottom of the frame
//wxBoxSizer* sizer = new wxBoxSizer(wxALIGN_CENTER_VERTICAL);
//sizer -> Add(login, 1, wxEXPAND | wxALL, 10);
wxButton* exit = new wxButton(panel, wxID_OK, "Exit", wxPoint(340, 390), wxSize(100, 35));
exit->Bind(wxEVT_BUTTON, &MainFrame::OnExitClicked, this);
panel->SetFont(panel->GetFont().Scale(2.5));
wxStaticText* staticText = new wxStaticText(panel, wxID_ANY, "UNO DELUXE", wxPoint(300, 40));
panel->SetFont(panel->GetFont().Scale(0.5));
wxStaticText* username = new wxStaticText(panel, wxID_ANY, "Enter username: ", wxPoint(180, 250));
wxTextCtrl* textCtrl = new wxTextCtrl(panel, wxID_ANY, " ", wxPoint(300, 250), wxSize(250, -1));
wxStaticText* password = new wxStaticText(panel, wxID_ANY, "Enter password: ", wxPoint(180, 290));
wxTextCtrl* textCtrl2 = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(300, 290), wxSize(250, -1), wxTE_PASSWORD);
}
I can't help you with the program not being able to find the image - the image needs to exist either in the application's working folder or on an absolute path.
You can get around this by including the image in the executable. Unfortunately there is no consistent cross-platform way to do this - windows and mac have separate ways of doing this but GTK has no way at all. Here are some suggestions from the wiki for some possible workarounds.
Regardless of the method used to get the image into the application, you should not draw it with a client DC. Instead you should handle the paint event for the panel and draw the image in the handler for that event. Here's an example:
#include <wx/wx.h>
class ImageBgFrame: public wxFrame
{
public:
ImageBgFrame(wxFrame *frame, const wxString& title);
private:
void OnImagePanelPaint(wxPaintEvent&);
void OnExit(wxCommandEvent&);
void CreateScaledBg();
wxPanel* m_imagePanel;
wxImage m_image;
wxBitmap m_scaledBg;
};
ImageBgFrame::ImageBgFrame(wxFrame *frame, const wxString& title)
:wxFrame(frame, wxID_ANY, title)
{
::wxInitAllImageHandlers();
// Try to load the image.
m_image = wxImage("Thinking-of-getting-a-cat.png", wxBITMAP_TYPE_PNG);
if ( !m_image.IsOk() )
{
return;
}
// Create the controls.
m_imagePanel = new wxPanel(this, wxID_ANY);
CreateScaledBg();
wxButton* exit = new wxButton(m_imagePanel, wxID_OK, "Exit");
// Arrang the controls with a sizer.
wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
szr->Add(exit,wxSizerFlags().Border(wxALL));
m_imagePanel->SetSizer(szr);
Layout();
// Bind Event Handlers
m_imagePanel->Bind(wxEVT_PAINT, &ImageBgFrame::OnImagePanelPaint, this);
exit->Bind(wxEVT_BUTTON, &ImageBgFrame::OnExit, this);
}
void ImageBgFrame::OnImagePanelPaint(wxPaintEvent&)
{
if ( m_imagePanel->GetSize() != m_scaledBg.GetSize() )
{
CreateScaledBg();
}
wxPaintDC dc(m_imagePanel);
dc.DrawBitmap(m_scaledBg,0,0);
}
void ImageBgFrame::OnExit(wxCommandEvent&)
{
Close();
}
void ImageBgFrame::CreateScaledBg()
{
wxSize sz = m_imagePanel->GetSize();
m_scaledBg = wxBitmap(m_image.Scale(sz.GetWidth(), sz.GetHeight(),
wxIMAGE_QUALITY_NORMAL));
}
class ImageBgApp : public wxApp
{
public:
bool OnInit()
{
ImageBgFrame* frame = new ImageBgFrame(NULL, "Image BG");
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(ImageBgApp);
This assumes that the image "Thinking-of-getting-a-cat.png" is in the application's working folder. On GTK it looks like this:
I've also made a few other changes to the code you gave:
I'm using Bind to connect the event handlers instead of static event tables.
I'm using default sizes for the controls instead of setting the size explicitly and laying out the controls in a sizer instead of setting explicit location.
Both of these changes are a more modern way of doing things.

Why does the second wxSizer fail to center the button?

I created a global panel and called a method that creates a sizer and a button. The button clears the sizer (i.e., also the panel), and then deletes is. Then, another method is called, using the same logic, it creates another sizer and another button. This time they don't work.
my code(windows, vs studio):
#include "MainFrame.h"
#include <wx/wx.h>s
MainFrame::MainFrame(const wxString& title) : wxFrame(nullptr, wxID_ANY, title)
{
panel = new wxPanel(this);
StartParty(panel);
}
void MainFrame::ClearButtonClicked(wxCommandEvent& evt)
{
panel->GetSizer()->Clear(true);
panel->SetSizerAndFit(nullptr);
ChooseMode(panel);
}
void MainFrame::StartParty(wxPanel* parent)
{
wxButton* start_button = new wxButton(parent, wxID_ANY, "Start the Party!", wxDefaultPosition, wxSize(200, 70));
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->AddStretchSpacer(1);
sizer->Add(
start_button,
0,
wxALL | wxALIGN_CENTER,
0);
sizer->AddStretchSpacer(1);
parent->SetSizerAndFit(sizer);
start_button->Bind(wxEVT_BUTTON, &MainFrame::ClearButtonClicked, this);
}
void MainFrame::ChooseMode(wxPanel* parent)
{
wxButton* select_button = new wxButton(parent, wxID_ANY, "Choose", wxDefaultPosition, wxSize(200, 70));
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->AddStretchSpacer(1);
sizer->Add(
select_button,
0,
wxALL | wxALIGN_CENTER,
0);
sizer->AddStretchSpacer(1);
parent->SetSizer(sizer);
}
You do need Layout(), as mentioned in the comments, as this is what actually repositions the windows -- SetSizer() just specifies the sizer to use for doing it, but doesn't do anything on its own immediately (it will when the window is resized the next time, as this results in a call to Layout()).
However, even if it's somewhat unrelated to the question itself, I think you shouldn't be doing this at all and use wxSimplebook instead. This simple (sic) class allows you to add a few pages to it and then easily switch between them.

How to replace background image and wxBoxSizer in the same frame?

In my mainwindow, I am setting the background Image and added few buttons using the wxBoxSizer in CreateWidgets. Now on clicking one of the buttons, it should replace the background Image and hide the old wxBoxSizer and create a new one.
After setting the new image it is always calling child OnPaint function not the parent OnPaint function.
MainFrame::MainFrame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title)
{
// set the frame icon
SetIcon(wxICON(sample));
wxString fileName = wxT("./images/image1.png");
LoadBitmap(fileName);
SetSize(size);
CreateWidgets();
}
void MainFrame::LoadBitmaps(wxString filename) {
if (!wxFile::Exists(filename))
wxLogWarning("Can't find image files");
wxImage::AddHandler(new wxPNGHandler);
image.LoadFile(filename);
size.SetHeight(image.GetHeight() + 60);
size.SetWidth(image.GetWidth() + 25);
}
void MainFrame::OnPaint(wxPaintEvent& WXUNUSED(evt))
{
wxPaintDC dc(this);
wxBitmap bitmap(image);
dc.DrawBitmap(bitmap, 5, 5, false);
}
void MainFrame::CreateWidgets() {
wxBoxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
std::vector<wxString> fileNames;
fileNames.push_back(wxT("./images/img.png"));
std::vector<wxString> rollover_files;
rollover_files.push_back(wxT("./images/rollover_img.png"));
std::vector<wxImage> images;
wxImage image1(fileNames[0]);
wxImage rollover_image1(rollover_files[0]);
wxBitmap bitmap1(image1);
wxBitmap rollover_bitmap1(rollover_image1);
const long id = 6000;
MWCustomBitmap *bitmapCtrl1 = new MWCustomBitmap(this, bitmap1, TetraMeshingBitMapBtn, rollover_bitmap1, NULL, wxDefaultPosition, wxDefaultSize);
button_sizer->Add(bitmapCtrl1, 0, wxALL, 10);
main_sizer->AddSpacer(350);
main_sizer->Add(button_sizer, wxSizerFlags().Center());
this->SetSizer(main_sizer);
}
Here on clicking button Loadfile(filename) is not working.
void MWCustomBitmap::OnBtnClicked(wxCommandEvent &evt) {
wxString filename = wxT("./images/img3.png");
if (!wxFile::Exists(filename))
wxLogWarning("Can't find image files");
wxImage::AddHandler(new wxPNGHandler);
((MainFrame*)(m_pParentwindow))->GetImage().LoadFile(filename);
}
PS: I am learning wxWidgets.

wxDialog loses stay on top attribute

I have 2 wxDialogs with the button which makes each of them top most dialog(wxSTAY_ON_TOP) after button click.
Workflow:
I make one of them top most, checked - dialog become top most.
I clicked once more and dialog become not top most, checked - ok.
Made both of them top most, checked - ok, both top most.
Made one of them not top most after step 3, checked - accordinly to logs 1 - top most, another is not top most, but if I click somewhere both behaves like not top most. I have tried with wxFrame, with more than 2 wxDialogs - result same. Maybe I missing something or threre is a bug. Please help.
wxWidgets 3.0.2, Windows
Simple frame which owns dialogs
class Frame : public wxFrame
{
public:
Frame()
: wxFrame(nullptr, wxID_ANY, wxEmptyString, wxPoint(1100, 300), wxSize(200, 200))
{
wxBoxSizer* sz = new wxBoxSizer(wxVERTICAL);
wxStaticText* text = new wxStaticText(this, wxID_ANY, "Parent Frame");
sz->Add(text, 0, wxALL, 5);
SetSizer(sz);
Layout();
}
};
Dialog class
class Dialog : public wxDialog
{
public:
Dialog(wxWindow* parent, wxPoint pos)
: wxDialog(parent, wxID_ANY, "", pos, wxSize(200, 200),
wxFRAME_NO_TASKBAR | wxTAB_TRAVERSAL | wxBORDER_NONE)
{
wxBoxSizer* mainS = new wxBoxSizer(wxVERTICAL);
wxButton* button = new wxButton(this, wxID_ANY, "Top Most");
mainS->Add(button, 0, wxALL, 5);
SetSizer(mainS);
Layout();
button->Bind(wxEVT_BUTTON, &Dialog::onButton, this);
}
void onButton(wxCommandEvent&)
{
bool isTopMost = (GetWindowStyle() & wxSTAY_ON_TOP) != 0;
if (isTopMost) {
// Makes not top most dynamically
SetWindowStyle(GetWindowStyle() & ~wxSTAY_ON_TOP);
}
else {
// Makes top most dynamically
SetWindowStyle(GetWindowStyle() | wxSTAY_ON_TOP);
}
}
};
Calling sample
class WxguiApp : public wxApp
{
public:
bool OnInit() override
{
Frame* mainWnd = new Frame();
mainWnd->Show();
SetTopWindow(mainWnd);
Dialog* dlg1 = new Dialog(mainWnd, wxPoint(500, 300));
dlg1->Show();
Dialog* dlg2 = new Dialog(mainWnd, wxPoint(800, 300));
dlg2->Show();
return true;
}
};
IMPLEMENT_APP(WxguiApp);

wxWidgets C++ panel is invisible

I am starting to practise my skills with wxWidgets and I wanted to add some panel with two different texts above this slider, but when I tried, the result is invisible:
MainWindow::MainWindow(const wxString& title) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition) {
wxMenuBar *menubar;
wxMenu *file;
menubar = new wxMenuBar;
file = new wxMenu;
file->Append(wxID_OPEN, wxT("&Open"));
menubar->Append(file, wxT("&File"));
SetMenuBar(menubar);
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
this->SetSizer(vbox);
player_widget = new wxWindow(this, wxID_ANY);
player_widget->SetBackgroundColour(wxColour(wxT("black")));
vbox->Add(player_widget, 1, wxEXPAND | wxALIGN_TOP);
wxBoxSizer* bs = new wxBoxSizer(wxHORIZONTAL);
wxPanel* p1 = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(0,20));
p1->SetSizer(bs);
p1->Enable(true);
p1->Show(true);
vbox->Add(p1,0,wxEXPAND);
wxStaticText* text1 = new wxStaticText(this,11, "text 1");
wxStaticText* text2 = new wxStaticText(this,12, "text 2");
bs->Add(text1);
bs->Add(text2);
timeline = new wxSlider(this, myID_TIMELINE, 0, 0, TIMELINE_MAX);
vbox->Add(timeline, 0, wxEXPAND);
wxPanel *controlPanel = new wxPanel(this, wxID_ANY);
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
controlPanel->SetSizer(hbox);
vbox->Add(controlPanel, 0, wxEXPAND);
playpause_button = new wxButton(controlPanel, myID_PLAYPAUSE, wxT("Play"));
stop_button = new wxButton(controlPanel, myID_STOP, wxT("Stop"));
volume_slider = new wxSlider(controlPanel, myID_VOLUME, VOLUME_MAX, 0, VOLUME_MAX, wxDefaultPosition, wxSize(100, -1));
hbox->Add(playpause_button);
hbox->Add(stop_button);
hbox->AddStretchSpacer();
hbox->Add(volume_slider);
}
Any ideas what I did wrong?
Change parent of text1 and text2 to p1.
It is simpler to use just one panel that holds everything.
Something like this ( a complete program )
#include <wx/wx.h>
#include <wx/app.h>
class cApp : public wxApp
{
public:
virtual bool OnInit();
};
#define TIMELINE_MAX 100
#define VOLUME_MAX 100
enum {
myID_TIMELINE,
myID_PLAYPAUSE,
myID_STOP,
myID_VOLUME
};
class cFrame: public wxFrame
{
wxWindow * player_widget;
wxSlider * timeline;
wxSlider * volume_slider;
wxButton * playpause_button;
wxButton * stop_button;
public:
cFrame(wxFrame *frame, const wxString& title)
: wxFrame(frame, -1, title, wxPoint(-1,-1),wxSize(600,600))
{
wxMenuBar *menubar;
wxMenu *file;
menubar = new wxMenuBar;
file = new wxMenu;
file->Append(wxID_OPEN, wxT("&Open"));
menubar->Append(file, wxT("&File"));
SetMenuBar(menubar);
// define one panel to hold everything
// make it big enough to fill the frame
wxPanel* p1 = new wxPanel(this,wxID_ANY, wxPoint(-1,-1),wxSize(600,600) );
// top level sizer to hold everything
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
// add window widget at top
player_widget = new wxWindow(p1, wxID_ANY);
player_widget->SetBackgroundColour(wxColour(wxT("black")));
vbox->Add(player_widget, 1, wxEXPAND | wxALIGN_TOP);
// add some texts in a horizontal row
wxBoxSizer* bs = new wxBoxSizer(wxHORIZONTAL);
wxStaticText* text1 = new wxStaticText(p1,11, "text 1");
wxStaticText* text2 = new wxStaticText(p1,12, "text 2");
bs->Add(text1);
bs->Add(text2);
// add texts just below window widget
vbox->Add( bs );
// add slider below texts
timeline = new wxSlider(this, myID_TIMELINE, 0, 0, TIMELINE_MAX);
vbox->Add(timeline, 0, wxEXPAND);
// some more controls in a row
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
playpause_button = new wxButton(p1, myID_PLAYPAUSE, wxT("Play"));
stop_button = new wxButton(p1, myID_STOP, wxT("Stop"));
volume_slider = new wxSlider(p1, myID_VOLUME, VOLUME_MAX,
0, VOLUME_MAX, wxDefaultPosition, wxSize(100, -1));
hbox->Add(playpause_button);
hbox->Add(stop_button);
hbox->AddStretchSpacer();
hbox->Add(volume_slider);
// add controls below big slider
vbox->Add( hbox );
// make everything happen
SetSizer(vbox);
}
};
IMPLEMENT_APP(cApp);
bool cApp::OnInit()
{
cFrame* frame = new cFrame(0L, _("wx Starter"));
frame->Show();
return true;
}
This gives:
Here's the thing about computer programming: at some time you always have to settle down and write some code. All those applications, like wxFormBuilder, that promise to write code for you have to be abandoned at some point and you actually have to do some work ( Here is a link to more about this )