How to draw a transparent rectangle over an existing drawing? Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to draw a transparent rectangle over an existing drawing?

Post by apoorv569 »

doublemax wrote: Did you Bind the event handler to the panel?
I bind the event like this,

Code: Select all

    Bind(wxEVT_KEY_UP, &WaveformViewer::OnControlKeyUp, this);
Should I remove "this" ?
doublemax wrote: You need to all event.Skip() in the keyboard event handler.
Also added event.Skip(),

Code: Select all

void WaveformViewer::OnControlKeyUp(wxKeyEvent &event)
{
    wxLogDebug("Key up");

    switch (event.GetKeyCode())
    {
        case WXK_CONTROL:
            if (bSelectRange)
            {
                SetCursor(wxCURSOR_ARROW);
                bSelectRange = false;
                bDrawSelectedArea = false;
                ReleaseMouse();
                return;
            }
            break;
        default:
            break;
    }

    event.Skip();    // <--------
}
Still behaves the same.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to draw a transparent rectangle over an existing drawing?

Post by doublemax »

Do you call event.Skip() in the mouse down handler? That's necessary, otherwise the panel doesn't get keyboard focus.
Use the source, Luke!
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to draw a transparent rectangle over an existing drawing?

Post by apoorv569 »

doublemax wrote: Sun Aug 08, 2021 5:27 pm Do you call event.Skip() in the mouse down handler? That's necessary, otherwise the panel doesn't get keyboard focus.
I added event.Skip() in mouse down, now it works nicely. Stops when I release CTRL and keeps drawing while I'm holding it.

I also added OnControlKeyDown(),

Code: Select all

void WaveformViewer::OnControlKeyDown(wxKeyEvent &event)
{
    wxLogDebug("Key down");

    switch (event.GetKeyCode())
    {
        case WXK_CONTROL:
            SetCursor(wxCURSOR_IBEAM);
            break;
        default:
            SetCursor(wxCURSOR_ARROW);
            break;
    }

    event.Skip();
}
But this doesn't get trigger either, do I need another event.Skip() somewhere for this one to work?
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to draw a transparent rectangle over an existing drawing?

Post by doublemax »

It probably triggers after you've clicked into the waveform panel. But as you can't guarantee that the panel has keyboard focus, leave that feature for later ;)
Use the source, Luke!
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to draw a transparent rectangle over an existing drawing?

Post by apoorv569 »

doublemax wrote: Sun Aug 08, 2021 6:06 pm It probably triggers after you've clicked into the waveform panel. But as you can't guarantee that the panel has keyboard focus, leave that feature for later ;)
I see, I'll leave this one for now.

What about this one, is something like this possible?
apoorv569 wrote: Also I would like to know if there is a event that I can catch to detect that the selected area has been drawn, i.e as soon as I click CTRL + LMB drag and draw selection, release mouse and the area gets drawn, it should trigger a event that I can catch, and in that I want to return its coordinates so I can calculate the position in time of the sample, so I can perform an action. Or perhaps as soon as the selection area is drawn, it returns true in a bool variable, and I can, in my MainFrame class check if this variable is true, then do something, but it needs to be triggered automatically. Is there such a event?
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to draw a transparent rectangle over an existing drawing?

Post by doublemax »

apoorv569 wrote: Also I would like to know if there is a event that I can catch to detect that the selected area has been drawn, i.e as soon as I click CTRL + LMB drag and draw selection, release mouse and the area gets drawn, it should trigger a event that I can catch, and in that I want to return its coordinates so I can calculate the position in time of the sample, so I can perform an action. Or perhaps as soon as the selection area is drawn, it returns true in a bool variable, and I can, in my MainFrame class check if this variable is true, then do something, but it needs to be triggered automatically. Is there such a event?
There is no such event. I'm also not quite sure what you're tying to do, but it sounds dirty. A paint event handler should be totally "passive", it should have no influence on other parts of the application. If the paint event handler just draws a "state", every information you need is also available outside the paint event handler.
Use the source, Luke!
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to draw a transparent rectangle over an existing drawing?

Post by apoorv569 »

doublemax wrote: There is no such event. I'm also not quite sure what you're tying to do, but it sounds dirty. A paint event handler should be totally "passive", it should have no influence on other parts of the application. If the paint event handler just draws a "state", every information you need is also available outside the paint event handler.
Image

I basically want that, when I hold CTRL + LMB and draw the selection, as soon as the LMB is released, i.e the selection area is draw, I want to take the x coordinates of the left and right part of the selection marked with arrows on the top, to be calculated according to the sample length, which in the case of this sample selected in the screenshot is 8 seconds, so say the selection is from 3 seconds to 5 seconds, both those text controls should get filled with 0:03 0:05 receptively. I'm treating these as loop points, from where to where the sample should loop between.

I already have the calculations set up, just want something that triggers this automatically.

Code: Select all

WaveformViewer::LoopPoints WaveformViewer::GetLoopPoints()
{
    Database db(m_InfoBar);

    int selected_row = m_Library.GetSelectedRow();

    if (selected_row < 0)
        return { 0.0f, 0.0f };

    wxString selected = m_Library.GetTextValue(selected_row, 1);
    std::string path = db.GetSamplePathByFilename(m_DatabaseFilepath, selected.BeforeLast('.').ToStdString());

    Tags tags(path);

    int length = tags.GetAudioInfo().length;

    // double position = m_MediaCtrl.Tell();

    int panel_width = this->GetSize().GetWidth();
    // double line_pos = panel_width * (position / length);

    int a = m_AnchorPoint.x, b = m_CurrentPoint.x;

    double loopA = ((double)a / panel_width) * length;
    double loopB = ((double)b / panel_width) * length;

    return { loopA, loopB };
}
And then I can maybe in the MainFrame class do something like,

Code: Select all

    wxLongLong loopA = m_TopWaveformPanel->GetLoopPoints().A;
    wxLongLong loopB = m_TopWaveformPanel->GetLoopPoints().B;

    int loopA_min = static_cast<int>((loopA / 60000).GetValue());
    int loopA_sec = static_cast<int>(((loopA % 60000) / 1000).GetValue());
    int loopB_min = static_cast<int>((loopB / 60000).GetValue());
    int loopB_sec = static_cast<int>(((loopB % 60000) / 1000).GetValue());

    wxLogDebug(wxString::Format("LoopA: %2i:%02i", loopA_min, loopA_sec));
    wxLogDebug(wxString::Format("LoopB: %2i:%02i", loopA_min, loopA_sec));

    if (m_TopWaveformPanel->IsAreaSelected())
    {
        m_LoopPointAText->SetValue(wxString::Format("%2i:%02i", loopA_min, loopA_sec));
        m_LoopPointBText->SetValue(wxString::Format("%2i:%02i", loopB_min, loopB_sec));
    }
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to draw a transparent rectangle over an existing drawing?

Post by doublemax »

I basically want that, when I hold CTRL + LMB and draw the selection, as soon as the LMB is released, i.e the selection area is draw, I want to take the x coordinates of the left and right part of the selection marked with arrows on the top
Why can't you do all that in the mouse up handler? Isn't that the "event" you need?
Use the source, Luke!
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to draw a transparent rectangle over an existing drawing?

Post by apoorv569 »

doublemax wrote: Sun Aug 08, 2021 7:59 pm
I basically want that, when I hold CTRL + LMB and draw the selection, as soon as the LMB is released, i.e the selection area is draw, I want to take the x coordinates of the left and right part of the selection marked with arrows on the top
Why can't you do all that in the mouse up handler? Isn't that the "event" you need?
I can, but the problem is those 2 text controls are not defined in WaveformViewer class, but in the MainFrame class, so do I just create a reference to a wxTextCtrl objects in the constructor of this class? I don't know any other way of using objects between classes.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to draw a transparent rectangle over an existing drawing?

Post by doublemax »

The cleanest solution would be to send a custom event which is received and processed by the main frame.

I think in this case the WaveformViewer is an immediate child of the main frame? Then you could also call a method in the main frame.
Use the source, Luke!
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to draw a transparent rectangle over an existing drawing?

Post by apoorv569 »

doublemax wrote: The cleanest solution would be to send a custom event which is received and processed by the main frame.
How to send a custom event from WaveformViewer to MainFrame?
doublemax wrote: I think in this case the WaveformViewer is an immediate child of the main frame? Then you could also call a method in the main frame.
I have WaveformViewer declared and defined as such,

MainFrame.hpp

Code: Select all

        WaveformViewer* m_TopWaveformPanel;
MainFrame.cpp

Code: Select all

    m_TopWaveformPanel = new WaveformViewer(this, m_TopPanel, *m_StatusBar, *m_Library, *m_MediaCtrl, *m_Timer, *m_InfoBar, m_ConfigFilepath, m_DatabaseFilepath);
I don't really like how I am passing all these other widgets in the constructor, but I don't enough experience to think of any other way to talk to these objects.
Clearly this class really should just have one parameter, wxWindow* window, but because of all the other widgets that I need to have in the WaveformViewer class, but are defined in MainFrame class I have to use all these references.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to draw a transparent rectangle over an existing drawing?

Post by doublemax »

How to send a custom event from WaveformViewer to MainFrame?
https://docs.wxwidgets.org/trunk/overvi ... nts_custom

Code: Select all

m_TopWaveformPanel = new WaveformViewer(this, m_TopPanel, *m_StatusBar, *m_Library, *m_MediaCtrl, *m_Timer, *m_InfoBar, m_ConfigFilepath, m_DatabaseFilepath);
Whatever you do, don't manipulate these controls directly. This is totally inflexible if anything changes in the future. Have a public method in the main frame that does what you want.

Assuming the main frame is the parent of the WaveformViewer:

Code: Select all

MainFrame *mainframe = wxDynamicCast(GetParent(), MainFrame);
if( mainFrame != NULL )
{
  mainFrame->UpdateGui( p1, p2, p3, ...);
}
Use the source, Luke!
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to draw a transparent rectangle over an existing drawing?

Post by apoorv569 »

I had a look at this link, just looking and reading at first glance, it does not seem that difficult. Though which route do you recommend I should go, using existing event class (if so which one would it be), or my own custom class?
doublemax wrote:

Code: Select all

m_TopWaveformPanel = new WaveformViewer(this, m_TopPanel, *m_StatusBar, *m_Library, *m_MediaCtrl, *m_Timer, *m_InfoBar, m_ConfigFilepath, m_DatabaseFilepath);
Whatever you do, don't manipulate these controls directly. This is totally inflexible if anything changes in the future. Have a public method in the main frame that does what you want.
I see, these widgets that I'm passing I'm either getting some value or setting nothing more.
doublemax wrote: Assuming the main frame is the parent of the WaveformViewer:

Code: Select all

MainFrame *mainframe = wxDynamicCast(GetParent(), MainFrame);
if( mainFrame != NULL )
{
  mainFrame->UpdateGui( p1, p2, p3, ...);
}
Is this example for managing the problem I mentioned above? about me passing all those parameters in the constructor? If so, wouldn't it cause me trouble, if I cross include both files in each other?
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to draw a transparent rectangle over an existing drawing?

Post by doublemax »

apoorv569 wrote: Tue Aug 10, 2021 12:27 am
I had a look at this link, just looking and reading at first glance, it does not seem that difficult. Though which route do you recommend I should go, using existing event class (if so which one would it be), or my own custom class?
That depends on how many values you need to transport. An existing event can only carry an integer and a string.
apoorv569 wrote: Tue Aug 10, 2021 12:27 am
doublemax wrote: Assuming the main frame is the parent of the WaveformViewer:

Code: Select all

MainFrame *mainframe = wxDynamicCast(GetParent(), MainFrame);
if( mainFrame != NULL )
{
  mainFrame->UpdateGui( p1, p2, p3, ...);
}
Is this example for managing the problem I mentioned above? about me passing all those parameters in the constructor? If so, wouldn't it cause me trouble, if I cross include both files in each other?
It can cause trouble, but there are solutions for that.
Use the source, Luke!
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to draw a transparent rectangle over an existing drawing?

Post by apoorv569 »

doublemax wrote: That depends on how many values you need to transport. An existing event can only carry an integer and a string.
At the moment I only want to transfer 2 values of type double. Can't say for future use.
doublemax wrote: It can cause trouble, but there are solutions for that.
Solutions like?
Post Reply