Adding controls to wxpanel takes a long time

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
Post Reply
iiuufigtt
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Feb 21, 2021 6:01 am

Adding controls to wxpanel takes a long time

Post by iiuufigtt »

I have a wxpanel that contains many kinds of controls, such as wxButton, wxTextCtrl, wxSpin, wxChoice, etc.

When showing the panel on Windows OS, it takes several seconds to initialize the controls and you can actually see the controls being rendered on the left top corner of the panel.

On Mac OS, there is no such rendering or delay, the panel is shown instantly.

Why on Windows OS, the controls are unnecessarily rendered before the panel is properly shown? Is there any way to avoid this and make the panel show quickly as in Mac OS.

Please see the screen capture under the Window OS in the attached video.
Attachments
p2.jpg
p1.jpg
p1.jpg (72.05 KiB) Viewed 3602 times
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4182
Joined: Sun Jan 03, 2010 5:45 pm

Re: Adding controls to wxpanel takes a long time

Post by PB »

I find hard to believe that creating those not really many controls takes so much time, unless the wxChoices there have high thousands of items.

What functions did the profiler report to take the most time?

Are you sure the issue is not related to resizing the drawing panel? What is the sizer type (wxGridBagSizer?) and hierarchy for the controls panel?

EDIT Here is a similar layout that takes no time to display on Win10 (wxWidgets master)
wxphotomagic.png
wxphotomagic.png (18.14 KiB) Viewed 3575 times

Code: Select all

#include <wx/wx.h>
#include <wx/spinctrl.h>
#include <wx/statline.h>

class MyFrame : public wxFrame
{
public:
    MyFrame() : wxFrame(nullptr, wxID_ANY, "Test")
    {
        wxBoxSizer* mainSizer = new wxBoxSizer(wxHORIZONTAL);
                
        mainSizer->Add(CreateControlPanel(), wxSizerFlags().Expand());
        
        wxPanel* drawingPanel = new wxPanel(this);              
        drawingPanel->SetBackgroundColour(*wxBLACK);
        mainSizer->Add(drawingPanel, wxSizerFlags().Proportion(1).Expand());
        
        SetSizer(mainSizer);
        
        SetClientSize(FromDIP(wxSize(800, 500)));
    }
protected:
    wxPanel* CreateControlPanel()
    {
        wxPanel* panel = new wxPanel(this);
        wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
        wxSizer* twoColSizer = new wxFlexGridSizer(2);

        auto AddCtrlWithLabel=[twoColSizer, panel](wxWindow* wnd, const wxString& label)
        {            
            twoColSizer->Add(new wxStaticText(panel, wxID_ANY, label), wxSizerFlags().Border().CenterVertical().Expand());
            twoColSizer->Add(wnd, wxSizerFlags().Border().CenterVertical().Expand());
        };
                
        wxStaticText* panelLabel = new wxStaticText(panel, wxID_ANY, "Photo Magic", 
                                                    wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
        wxFont        font = panelLabel->GetFont();

        panelLabel->SetFont(font.MakeBold().MakeLarger());
        mainSizer->Add(panelLabel, wxSizerFlags().Expand().DoubleBorder());
        
        wxChoice*     methodChoice = new wxChoice(panel, wxID_ANY);
        wxArrayString methodChoiceItems;
        
        for ( size_t i = 0; i < 100; ++i )
            methodChoiceItems.push_back(wxString::Format("Item #%zu", i));
        methodChoiceItems.push_back("Puzzle"); 
        methodChoice->Select(methodChoice->Append(methodChoiceItems));        
        AddCtrlWithLabel(methodChoice, "Method");

        AddCtrlWithLabel(new wxButton(panel, wxID_ANY, "Browse"), "Select Photo");
        AddCtrlWithLabel(new wxTextCtrl(panel, wxID_ANY, "100"), "Model size X");
        AddCtrlWithLabel(new wxTextCtrl(panel, wxID_ANY, "77.3"), "Model size Y");
        AddCtrlWithLabel(new wxSpinCtrl(panel, wxID_ANY, "4"), "X section");
        AddCtrlWithLabel(new wxSpinCtrl(panel, wxID_ANY, "4"), "Y section");
        AddCtrlWithLabel(new wxTextCtrl(panel, wxID_ANY, "2"), "Min Thickness");
        AddCtrlWithLabel(new wxTextCtrl(panel, wxID_ANY, "5"), "Max Thickness");
        AddCtrlWithLabel(new wxTextCtrl(panel, wxID_ANY, "0.1"), "Resolution");
        AddCtrlWithLabel(new wxTextCtrl(panel, wxID_ANY, "0.5"), "Edge chamfer");
        AddCtrlWithLabel(new wxTextCtrl(panel, wxID_ANY, "-0.2"), "Tolerance");
        AddCtrlWithLabel(new wxStaticText(panel, wxID_ANY, "80 B"), "File size");

        mainSizer->Add(twoColSizer);

        mainSizer->Add(new wxStaticLine(panel), wxSizerFlags().Expand().Border());

        wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);            
        buttonSizer ->Add(new wxButton(panel, wxID_ANY, "Save model"), wxSizerFlags().Proportion(1).Border().Expand());
        buttonSizer ->Add(new wxButton(panel, wxID_ANY, "Close"), wxSizerFlags().Proportion(1).Border().Expand());
        mainSizer->Add(buttonSizer, wxSizerFlags().Expand());

        panel->SetSizer(mainSizer);
        return panel;
    }
};

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {
        (new MyFrame)->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
Therefore it is clear that the information your provided is insufficient to pinpoint the issue. As always, an SSCEE (see e.g. my code above) would be best. It also useful to provide wxWidgets version used.

For starters, you could do the usual, comment out most of the control creation code and assuming it helps, add them back one by one until it "breaks". Perhaps commenting out the drawing panel part would be useful too. Removing the menu and status bar to see if there is not some weird interaction should be done at last, as these look like unlikely culprits.
iiuufigtt
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Feb 21, 2021 6:01 am

Re: Adding controls to wxpanel takes a long time

Post by iiuufigtt »

Thank you for your reply.

There are around 180 controls on the wxPanel. Depending on which method is chosen, some controls are shown and others are hidden.

During control creation, I can see all controls being draw one by one. Of course, the drawing time of one control is very short but when added up, the total time is several seconds.

This issue only happens under Windows OS. On Mac OS, I cannot see the control being drawn. The panel is shown immediately and the controls that are supposed to be shown are in place.

I think the key problem is the drawing of the controls during creation. It should not happen on screen.

I use wxBoxSizer and wxFlexGridSizer. No other sizer is used.

You can try the program and see the effect yourself.

Go to www.luban3d.com
Scroll down to Download LuBan section.
Follow either the Dropbox or Google drive download link.
In Win 64 folder, either download LuBan.exe and run it directly, or download setup.msi and install LuBan.

Run LuBan.
File -> Create -> Photo magic.
You will see how long it takes.
iiuufigtt
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Feb 21, 2021 6:01 am

Re: Adding controls to wxpanel takes a long time

Post by iiuufigtt »

In addition, this problem on Windows OS has been there for a long time.

I used wxWidgets-2.8 at the beginning, and now to wxWidgets-3.1.4, all have the same issue. I can see the controls being drawn on left top corner. Previously, I did not investigate and ask questions here because there are only 20 to 30 controls in each panel or dialogue box; so the delay is negligible. But my program has increased in capacity and now it has more than 100 controls, some panel has got nearly 200 controls. The delay becomes very noticable.
User avatar
doublemax
Moderator
Moderator
Posts: 19102
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Adding controls to wxpanel takes a long time

Post by doublemax »

I tested this and i can see the controls in the upper left corner, but the whole process takes only about 1 second. I can only assume that you're doing something "unusual" during control creation, but i have no idea, what. We'd need to see more code for that.

The parent of the controls on the left seems to be a wxScrolledWindow. Try changing that to a wxPanel for a test.

Try wrapping the control creation in Freeze() / Thaw() calls on the main wxFrame.

When you switch through the method, different controls are visible. Are they all direct siblings in the same sizer? How do yo show / hide them? I would suggest to put all controls for one method into a separate wxPanel, put them all into a vertical boxsizer and show show/hide only these panels when switching. (Or use a wxSimpleBook which does the same under the hood).
Use the source, Luke!
iiuufigtt
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Feb 21, 2021 6:01 am

Re: Adding controls to wxpanel takes a long time

Post by iiuufigtt »

Thank you for your reply. Please see my point by point response below. In general, I think there is something wrong with the Win version as the Mac version does not have the problem. You cannot see the controls being drawn in the Mac version; so the creation is very fast. One of your suggestions fixed the problem.

>> I tested this and i can see the controls in the upper left corner, but the whole process takes only about 1 second. I can only assume that you're doing something "unusual" during control creation, but i have no idea, what. We'd need to see more code for that.

I create the controls in the constructor of a wxPanel. This is what I do in the code.
// create a control
m_ptctrMSDi = new wxTextCtrl(this, ID_PM_BOX_SDI, ValToStr(m_dMSDi), wxDefaultPosition, wxSize(nLen2, wxDefaultCoord), wxTE_PROCESS_ENTER);
// add it to a sizer
m_pszBMS->Add(m_ptctrMSDi, 0, xALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
// lastly, add the sizer to wxPanel's main sizer. The delay (drawing) happens during control creation.

>> The parent of the controls on the left seems to be a wxScrolledWindow. Try changing that to a wxPanel for a test.

The parent of the controls is a wxPanel; the parent of the wxPanel is a wxScrolledWindow.

>> Try wrapping the control creation in Freeze() / Thaw() calls on the main wxFrame.

Awesome! This solution definitely has fixed the problem. The creation time is much reduced. I only tested it in Win OS. I will test if it has side effect on Mac OS.

Just a thought: is there any way to disable drawing in control creation, e.g. new wxSpinCtrl(...)? If yes, the problem would never occur.

>> When you switch through the method, different controls are visible. Are they all direct siblings in the same sizer? How do yo show / hide them? I would suggest to put all controls for one method into a separate wxPanel, put them all into a vertical boxsizer and show show/hide only these panels when switching. (Or use a wxSimpleBook which does the same under the hood).

Different controls are grouped by different sizers. They are all grouped by the main sizer of the wxPanel.
User avatar
doublemax
Moderator
Moderator
Posts: 19102
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Adding controls to wxpanel takes a long time

Post by doublemax »

iiuufigtt wrote: Thu Oct 21, 2021 9:00 am Just a thought: is there any way to disable drawing in control creation, e.g. new wxSpinCtrl(...)? If yes, the problem would never occur.
The strange things is that this doesn't happen for me. It only happens if you do anything that triggers polling the event loop, e.g. explicitly calling ::wxYield().

Do you do anything during control creation that might do that, directly or indirectly (e.g. in a sub-control)?
Different controls are grouped by different sizers. They are all grouped by the main sizer of the wxPanel.
I think you should still try my suggestion and check if it make any difference.

BTW: I was wondering why i've never seen that in any of my applications and it occurred to me that usually i create *all* controls, and only afterwards, the frame is shown (as PB's sample does, too). So it's impossible to see the controls during that time anyway. But in your case it's different because they're created when the frame is already visible.

However, i also created a sample that created the controls later, and i still didn't see the individual controls popping up, unless i explicitly call ::wxYield inside the loop.
Use the source, Luke!
iiuufigtt
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Feb 21, 2021 6:01 am

Re: Adding controls to wxpanel takes a long time

Post by iiuufigtt »

In my code, I first create a wxPanel.

m_pPanelPM = new Panel_PM(m_pSplitter, this);

Then in the constructor of the panel, I create all the controls and sizers.

m_ptctrCeMX = new wxTextCtrl(this, ID_PM_CEL_MX, ValToStr(m_dCeMX), wxDefaultPosition, wxSize(nLen2, wxDefaultCoord), wxTE_PROCESS_ENTER);

m_ptctrCeMY = new wxTextCtrl(this, ID_PM_CEL_MY, ValToStr(m_dCeMY), wxDefaultPosition, wxSize(nLen2, wxDefaultCoord), wxTE_PROCESS_ENTER);

At the end of the constructor function, I do this.

SetSizer(pszMain); // set the main sizer of the panel
SetScrollRate(0, 10); // set the scroll rate

I do not use wxYeild in any of my code.

The delay (drawing) happens at each call of new wxTextCtrl, or new any type of control.

I debugged into the creation function of a typical control and saw some code relating to window status "shown". By default a window is created with shown, not hide. I wonder if that trigged the drawing of the control on Win OS.

Anyway, on Mac, none of this happens.

I must thank you for the suggestion of Freeze / Thaw. It has solved the long-lasting problem that I am facing. Maybe there is a better way to write the code but this solution works.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7449
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Adding controls to wxpanel takes a long time

Post by ONEEYEMAN »

Hi,
It should be.
Which compiler do you use?
How did you build the library and your code?

Thank you.
Post Reply