Taking and saving screenshots

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.
Natulux
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 226
Joined: Thu Aug 03, 2017 12:20 pm

Re: Taking and saving screenshots

Post by Natulux » Wed Aug 30, 2017 1:57 pm

coderrc wrote:try this instead

Code: Select all


void MyFrame::OnMouse(wxMouseEvent& event)
{
   if(event.GetEventType() == wxEVT_LEFT_DOWN)
   {
      m_screenePointStart = wxGetMousePosition();
      
      if(!mb_LeftMouseDown && event.LeftIsDown()) // left button held down ie user is dragging
      {
         mb_LeftMouseDown = true;
         m_screenePointStart = wxGetMousePosition();
         wxWindow::ReleaseMouse();
         wxWindow::CaptureMouse();
      }

      wxString s = "";
      s << "LeftDown: " << mb_LeftMouseDown;
      wxLogMessage(s);
   }
   else if(event.GetEventType() == wxEVT_LEFT_UP)
   {
      wxLogMessage("LeftUp");
      if(mb_LeftMouseDown && event.LeftUp())
      {
         mb_LeftMouseDown = false;
         m_screenePointEnd = wxGetMousePosition();
         wxWindow::ReleaseMouse();
         FinishRectSave();
      }
   }
   else if(event.GetEventType() == wxEVT_RIGHT_DOWN)
   {
      wxLogMessage("RightDown");
   }
}

Nice try, I didn't know about those functions

Code: Select all

event.LeftUp() 
event.LeftDown()
Unfortunately, it makes no difference to the other methodes (like testing the eventType against the Flag). Both events (down and up) are, again, handled simultaniously.
coderrc wrote:you might also want to look at creating a custom "StartScreenShotEvent" in order to decouple the Menu Event from the Mouse Event
something like

Code: Select all

void MyTaskBarIcon::OnScreenshotRect(wxCommandEvent&)
{
   wxCommandEvent* evt = new wxCommandEvent(StartScreenShet);
   GetEventHandler()->QueueEvent(evt);
}

void MyTaskBarIcon::OnStartScreenShet(wxCommandEvent&)
{
  wxGetApp().m_frame->Screenshot_ToAppDir_rect();
}
I admit I never created an event myself, yet. When a new (temporary) event is created and queued, against what can I test this event? Or how to catch it? In the case of the predefined events, I have global wxEVT_X flags for this purpose.
Is the EventHandler global for my application? Or is it global for the class from which it is called? Because immediately after catching the event (of your example snipped), the class is changed and the rest is handled in the frame class.
Or is it even file dependent (unlikely)? Because the original code is of course not squeezed into one file.

Thanks for your efforts!

Greetings
Natu

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 4699
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Taking and saving screenshots

Post by ONEEYEMAN » Wed Aug 30, 2017 2:05 pm

Hi,
I did post the code for my project.

Check it out. If you have any questions - kust ask.

I wrote it a long time ago, but it will be easy enough to just open the code and look thru.

As I said it requires libcurl to work, so if you want to compile and run it, grab the library. Code is based on the wx-2.9.4.

Thank you.

coderrc
Earned some good credits
Earned some good credits
Posts: 141
Joined: Tue Nov 01, 2016 2:46 pm

Re: Taking and saving screenshots

Post by coderrc » Wed Aug 30, 2017 2:25 pm

for custom events, see here https://wiki.wxwidgets.org/Custom_Event ... xEventType

GetEventHandler is local to the current object context.
In other words
this->GetEventHandler()->QueueEvent(evt);
is the same as
GetEventHandler()->QueueEvent(evt);

I really only skimmed over your code. You will have to decide the best way to handle the custom event.
You can do it in the taksbar icon, like i showed earlier
or you can handle the event in the m_frame

Code: Select all

void MyTaskBarIcon::OnScreenshotRect(wxCommandEvent&)
{
   wxCommandEvent* evt = new wxCommandEvent(StartScreenShet);
   wxGetApp().m_frame->GetEventHandler()->QueueEvent(evt);
}
I'd probably prefer the latter. The Idea here is just to decouple the user action from the event queue thread.

what you have now is a situation like
user clicks the "do screenshot button"
-- menu event is added to event queue
MENU EVENT HANDLER STARTS
call to MyFrame::Screenshot_ToAppDir_rect()
call to MyFrame::ScreenshotRect(
call to capturemouse
MENU EVENT HANDLER ENDS

since there a mouse input required to fire the menu event, the capture mouse may be causing your mouse event handler to pick up that mouse click.

User avatar
doublemax
Moderator
Moderator
Posts: 15491
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Taking and saving screenshots

Post by doublemax » Wed Aug 30, 2017 6:04 pm

After testing this myself, i have to say sorry for putting you on the wrong track. I really believed that would work.

But reading up on MSDN:
Sets the mouse capture to the specified window belonging to the current thread.SetCapture captures mouse input either when the mouse is over the capturing window, or when the mouse button was pressed while the mouse was over the capturing window and the button is still down.
So you can't capture the mouse and then click somewhere outside your own window.

I have one or two alternative ideas, but i'll have to try them myself before i give another bad advice.

Sorry again.
Use the source, Luke!

Natulux
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 226
Joined: Thu Aug 03, 2017 12:20 pm

Re: Taking and saving screenshots

Post by Natulux » Thu Aug 31, 2017 6:08 am

Hey guys
ONEEYEMAN wrote:I did post the code for my project.
That sounds awesome. But ... where? Do you have a link? Thanks.
coderrc wrote:for custom events, see here https://wiki.wxwidgets.org/Custom_Event ... xEventType
ok, I will. Thanks.
coderrc wrote:you can handle the event in the m_frame
I'd probably prefer the latter. The Idea here is just to decouple the user action from the event queue thread.
This isn't about the event of clicking on the menu item to start the process, but rather the events of capturing the mouse-input for my rect, am I right?
Well, I've been thinking. If I use custom events for the capturing, wouldn't the mouse clicking then be handled by standard event handlers and lead to unwanted behaviour like changing focus to the klicked element and such?
Since I want to be able to take screenshots of whatever the user has on his desktop, surpressing the click while capturing the rect for the screenshot isn't such a bad idea.
I guess your point is, that I shouldn't capture mouse events from the whole application the whole time and to seperate the events, if I needed the mouse for normal button clicking as well es drawing the rect, am I right?

Or are there other reasons, why I should decouple it?
doublemax wrote:After testing this myself, i have to say sorry for putting you on the wrong track. I really believed that would work.
Im a little surprised, that this time it really wasn't just me, not beeing able to implement that. :D
No problem max, I am gratefull for every idea, especially if it is promising to get the job done. At least I now have an answer, when someone else tells me, that there is "this function, that I could have used" ;-)

Im still curious, how other freeware screenshot tools do the job, though...

Have a nice day
Natu

User avatar
doublemax
Moderator
Moderator
Posts: 15491
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Taking and saving screenshots

Post by doublemax » Thu Aug 31, 2017 7:34 am

You could open another, empty and borderless wxFrame in fullscreen mode, but 99% transparent. Then you should still get all mouse events, even without CaptureMouse.
Use the source, Luke!

Natulux
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 226
Joined: Thu Aug 03, 2017 12:20 pm

Re: Taking and saving screenshots

Post by Natulux » Thu Aug 31, 2017 9:49 am

doublemax wrote:You could open another, empty and borderless wxFrame in fullscreen mode, but 99% transparent. Then you should still get all mouse events, even without CaptureMouse.
That is almost, what I am doing right know. I have an almost borderless frame (has a gray top frame of maybe 5 pixel width and an overall blue frame of 1 pixel width). But I didn't know about transparency, so I made a screenshot and set that screenshot as a background picture onto my frame.

If I could use transparency, that would be a lot easier though.

Natulux
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 226
Joined: Thu Aug 03, 2017 12:20 pm

Re: Taking and saving screenshots

Post by Natulux » Thu Aug 31, 2017 10:18 am

About that transparency:
I saw an older article of yours: viewtopic.php?t=38022
doublemax wrote:The only other option i can think of, is to use a color key. But this would me MSW only. Check the attached modified "minimal" sample.
I used your modified minimal sample, but the frame in that case doesn't catch the events, because it lets you work on the desktop through the frame.

What you were suggesting here was to use

Code: Select all

SetTransparent()
am I right? And I apply only 99% so that I still click onto my frame and not onto the desktop?
I don't see how to set it only partly transparent, though.

[EDIT:] Ups, don't mind me. I was typing faster than I could think. The documebntation sais it all:
The parameter alpha is in the range 0..255 where 0 corresponds to a fully transparent window and 255 to the fully opaque one. The constants wxIMAGE_ALPHA_TRANSPARENT and wxIMAGE_ALPHA_OPAQUE can be used.
[EDIT2:] Uh this is no nice xD
Only some things left: If I have multiple monitor devices, the returned size is still that of the main monitor.

Code: Select all

//Create a DC for the whole screen area
wxScreenDC dcScreen;
//Get the size of the screen/DC
wxCoord screenWidth, screenHeight;
dcScreen.GetSize(&screenWidth, &screenHeight);
I can change the width by hand to strech my area from the main screen over to another screen, but then I would need to loop over all existing monitor devices and catch their size... and their position, because they might even be on the left and therefore have negative coordinates.
Or can you think of an easier solution?

And secondly, when I make a screenshot of a MSW directory with a file selected, its selection color is changed to pink.

And thirdly, a crosshair and/or a dashed border line of the rect while drawing it would be very nice, too.

[EDIT3:] I think it's safe to say, that the screenshot doesn't like transparency at all. See window frame in the second applied picture.

Greets
Natu
Attachments
027_image.jpg
pink transparency
027_image.jpg (53.13 KiB) Viewed 2533 times
031_image.jpg
destroyed window frame
031_image.jpg (50.74 KiB) Viewed 2533 times

User avatar
doublemax
Moderator
Moderator
Posts: 15491
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Taking and saving screenshots

Post by doublemax » Thu Aug 31, 2017 1:25 pm

Only some things left: If I have multiple monitor devices, the returned size is still that of the main monitor.
Unfortunately wxWidgets lacks proper multi-monitor support for wxScreenDC.
I can change the width by hand to strech my area from the main screen over to another screen, but then I would need to loop over all existing monitor devices and catch their size... and their position, because they might even be on the left and therefore have negative coordinates.
That's not that hard. I happen to have some code for that:

Code: Select all

wxRect _screen_rect;
int n = wxDisplay::GetCount();
for(int i=0; i<n; i++)
{
  wxDisplay d(i);
  wxRect r = d.GetGeometry();
  if(i==0)	_screen_rect=r;
  else			_screen_rect.Union(r);
}
I think it's safe to say, that the screenshot doesn't like transparency at all. See window frame in the second applied picture.
Did you hide the transparent overlay before making the screenshot? You may also have to call ::wxYield() to make sure that it's really gone.
And thirdly, a crosshair and/or a dashed border line of the rect while drawing it would be very nice, too.
You could try to use wxOverlay for that. It's used in ONEEYEMAN's code, but i have my doubts it'll work with wxScreenDC.

If that doesn't work, I would try to use another wxFrame for that. Use a shaped frame where only the "overlayed" parts (e.g. crosshair and selection rectangle) are visible.
Use the source, Luke!

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 4699
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Taking and saving screenshots

Post by ONEEYEMAN » Thu Aug 31, 2017 1:33 pm

Hi,
It is attached to one of my posts to this thread.
Just click the link and download it.

However, I can tell you right now that I did exactly what you are doing right now.
I got a screenshot, created a transparent window and use that bitmap as a background.

Then I handled the mouse click events.

Just look at the code - it will all be clear.

Thank you.

iwbnwif
Super wx Problem Solver
Super wx Problem Solver
Posts: 274
Joined: Tue Mar 19, 2013 8:52 pm

Re: Taking and saving screenshots

Post by iwbnwif » Thu Aug 31, 2017 1:52 pm

[EDIT3:] I think it's safe to say, that the screenshot doesn't like transparency at all. See window frame in the second applied picture.
I had similar problems as you can see in the YouTube video linked in this post:

viewtopic.php?f=1&t=41448#p169981

I believe the solution was to set the bitmap depth to 24 bit rather than use the default value (which was 32-bits).
wxWidgets 3.1.2, MinGW64 8.1.0, g++ 8.1.0, Ubuntu 19.04, Windows 10, CodeLite + wxCrafter
Some people, when confronted with a GUI problem, think "I know, I'll use Eclipse RCP". Now they have two problems.

Natulux
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 226
Joined: Thu Aug 03, 2017 12:20 pm

Re: Taking and saving screenshots

Post by Natulux » Fri Sep 01, 2017 6:22 am

doublemax wrote:Unfortunately wxWidgets lacks proper multi-monitor support for wxScreenDC.
Ah, thats a shame.
doublemax wrote:That's not that hard. I happen to have some code for that:

Code: Select all

wxRect _screen_rect;
int n = wxDisplay::GetCount();
for(int i=0; i<n; i++)
{
  wxDisplay d(i);
  wxRect r = d.GetGeometry();
  if(i==0)	_screen_rect=r;
  else			_screen_rect.Union(r);
}
That look promising. I'll try that, thanks! :-)
doublemax wrote:Did you hide the transparent overlay before making the screenshot? You may also have to call ::wxYield() to make sure that it's really gone.
To be honest, in this case the screenshots were taken not with the transparent frame, but with my approach with a screenshot of my desktop, set as the background image of the frame that then covers the desktop.
After having the the seccond coordinate of my frame, I use

Code: Select all

this->Show(false);
in the frame class. I might try wxYield() too.
doublemax wrote:You could try to use wxOverlay for that. It's used in ONEEYEMAN's code, but i have my doubts it'll work with wxScreenDC.
If that doesn't work, I would try to use another wxFrame for that. Use a shaped frame where only the "overlayed" parts (e.g. crosshair and selection rectangle) are visible.
I'll look into the solution of ONEEYEMAN to see, how he made it. If I use a frame with a panel (as I am doing right now with my background picture solution) and therefore have no transparency, I was able to draw a rect with an overlay and the function:

Code: Select all

void MyFrame::OnMotion( wxMouseEvent& event )
{
    int x = event.GetX();
    int y = event.GetY();

    if(m_rubberBand)
    {
        //draw a selection rectangle on the overlay
        wxClientDC dc( mpn_basicDrawPane );

        wxDCOverlay( m_overlay, &dc ).Clear();
        dc.SetPen( wxPen( *wxLIGHT_GREY, 2 ) );
        dc.SetBrush( *wxTRANSPARENT_BRUSH );
        dc.DrawRectangle( wxRect( m_screenePointStart, wxPoint(x,y) ) );
    }
}
But the drawn rectangle is invisible, if I make the frame transparent.

ok, now I got work to do :-) Thanks.

Natulux
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 226
Joined: Thu Aug 03, 2017 12:20 pm

Re: Taking and saving screenshots

Post by Natulux » Fri Sep 01, 2017 6:24 am

ONEEYEMAN wrote:Hi,
It is attached to one of my posts to this thread.
Just click the link and download it.

However, I can tell you right now that I did exactly what you are doing right now.
I got a screenshot, created a transparent window and use that bitmap as a background.

Then I handled the mouse click events.

Just look at the code - it will all be clear.

Thank you.
Oh my bad, last post on page 1. I didn't see it because I was posting right after you and I was on page 2, so I didn't look back. ;-)
Thank you ONEEYEMAN, I'll have the best parts of your code and befriend them with mine. xD

[EDIT:] I tried to install libcurl (version 7.55.1, RTMP, SSH2, SSL, SSPI Version) for my windows 10 system, but I won't install without "LIBEAY32.dll".
That ought to come with openSSL, so I installed x32 and x64 version of openSSL. Neither of those installations (version 1.1.0e) provided me with the needed dll (I chose it to be installed into my system32 directory - I searched for the missing dll system wide with a search tool, it is certainly not there). Now Im unable to compile your code, because the "curl.h" is missing. Do I need a legacy version or something?

[EDIT2:] Nevermind. I used the 1.0.2k openSSL and got my dlls. Now Im stuck at compiling with the

Code: Select all

error LNK1104: cannot open file 'libcurld_imp.lib'
That lib doesn't exist either. Do I have to compile libcurl from github first? I tried that, but it won't finish compiling:

Code: Select all

C:\libcurl\winbuild>nmake /f Makefile.vc mode=dll WITH_SSL=dll WITH_SSL=dll
[...]
..\lib\md5.c(88) : fatal error C1083: Datei (Include) kann nicht geöffnet werden (can't be opened): "openssl/md5.h": No such file or directory
This is going slightly over my head xD
Thanks.
Natu
Last edited by Natulux on Fri Sep 01, 2017 8:25 am, edited 4 times in total.

Natulux
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 226
Joined: Thu Aug 03, 2017 12:20 pm

Re: Taking and saving screenshots

Post by Natulux » Fri Sep 01, 2017 6:29 am

iwbnwif wrote:
[EDIT3:] I think it's safe to say, that the screenshot doesn't like transparency at all. See window frame in the second applied picture.
I had similar problems as you can see in the YouTube video linked in this post:

viewtopic.php?f=1&t=41448#p169981

I believe the solution was to set the bitmap depth to 24 bit rather than use the default value (which was 32-bits).
Yes!
That helped. Thank you iwbnwif :-)

Greets
Natu

Natulux
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 226
Joined: Thu Aug 03, 2017 12:20 pm

Re: Taking and saving screenshots

Post by Natulux » Tue Sep 12, 2017 8:15 am

Hey again

I was occupied for some time, but now I'm back on this topic. Since I am unable to run the example of ONEEYEMAN I'd like to understand the overlay a little more.
I have elements of a window class (a frame and a panel), which is drawn naturally. Now I can have an overlay attached to an element (the panel e.g.) to draw something on the screen, without changing the original element itself. Am I correct?

What I don't understand:
- Is the overlay drawing into the ScreenDC and is therefore also in my screenshot of the screenDC? If so, can I prevent that?
- When the overlay is attached to a frame (or an element of a frame) which is invisible(transparent), how is the overlay as a child not invisible?
- Has the wxOverlay to be attached to something or can it exist on its own?

[EDIT:]
This is a little confusing for me: I have a frame and a panel with the full size of my frame.

myframe.h

Code: Select all

wxPanel* mpn_basicDrawPane;
myframe.cpp

Code: Select all

MyFrame::MyFrame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title, wxPoint(10,10), wxSize(100,100), wxFULLSCREEN_ALL){
mpn_basicDrawPane = new wxPanel( this );

this->SetPosition(mp_multiMonTopLeft);
this->SetSize(mp_multiMonTopLeft.x,mp_multiMonTopLeft.y,mc_frameWidth, mc_frameHeight);
mpn_basicDrawPane->SetSize(mp_multiMonTopLeft.x,mp_multiMonTopLeft.y,mc_frameWidth, mc_frameHeight);
	
mpn_basicDrawPane->Bind( wxEVT_LEFT_DOWN, &MyFrame::OnMouse, this );
mpn_basicDrawPane->Bind( wxEVT_LEFT_UP, &MyFrame::OnMouse, this );
mpn_basicDrawPane->Bind( wxEVT_MOTION, &MyFrame::OnMotion, this );
mpn_basicDrawPane->Bind( wxEVT_RIGHT_DOWN, &MyFrame::OnMouse, this );
mpn_basicDrawPane->Bind( wxEVT_CHAR_HOOK,	&MyFrame::OnCharHook, this);
}

if(this->CanSetTransparent())
{
	this->SetTransparent(1);
	//mpn_basicDrawPane->SetTransparent(1); //doesn't work
}
As you can see, I have a frame, which is set transparent. On this frame is a panel, which holds the key bindings for the events (and the event table not shown here).
But I can't set the panel transparent, it only gets transparent as a child of a transparent frame.

Then again, I need to set the clientDC on my panel, not on my frame. Why?
myframe.cpp

Code: Select all

void MyFrame::OnMotion( wxMouseEvent& event )
{
    int x = event.GetX();
    int y = event.GetY();

    if(mb_rubberBand)
    {
		//draw a selection rectangle on the overlay
		wxClientDC dc( mpn_basicDrawPane ); //Works
		//wxClientDC dc(this); //doesnt work
		
		wxDCOverlay( m_overlay, &dc ).Clear();
		dc.SetBackgroundMode(0);
		dc.SetPen( wxPen( *wxLIGHT_GREY, 2, wxLONG_DASH ) );
		dc.SetBrush( *wxTRANSPARENT_BRUSH );
		dc.DrawRectangle( wxRect( wxPoint(m_screenePointStart.x - mp_multiMonTopLeft.x, m_screenePointStart.y), wxPoint(x,y) ) );
	}
}
Thank you.
Natu

Post Reply