How to make popup image? wxWidgets - c++

I'm trying to make a popup image preview window like it's done in Autodesk Revit Architecture:
The behaviour of popup image is:
When the mouse stops for 500 milliseconds over the truncated image, a full-sized popup image appears near the mouse cursor.
The Popup image is not a modal dialog, therefore controls of the main window(wxDialog) are still enabled.
The Popup window disappears on mouse movement.
I tried to do it, but i failed.
First I put wxStaticBitmap on wxDialog and use ShowModal() to show this full-sized image. It works great but as it's Modal, main window becomes disabled.
I tried to make this dialog not modal, but when I try to do it, main window raises(main window is modal) and image disappears.
upd.
Now my code:
class PictureFrame: public wxPopupTransientWindow
{
wxStaticBitmap *m_picture;
public:
PictureFrame( wxWindow *parent );
~PictureFrame();
};
Panel code structure is like this:
class MaterialsPane: public wxPanel
{
PictureFrame* m_popup;
wxTimer* m_timer;
public:
MaterialsPane( wxWindow* parent);
~MaterialsPane();
void OnTimer( wxTimerEvent& event);
void OnMouseMove( wxMouseEvent& event );
....
DECLARE_EVENT_TABLE()
};
Panel is placed in main modal dialog:
class MaterialsFrame: public wxDialog {
MaterialsPane* m_materialsPane;
public:
MaterialsFrame( wxWindow* parent, wxWindowID id = wxID_ANY);
~MaterialsFrame();
};
it helped but not completely. As image appears not under mouse cursor but near it (like in the picture of my question), popup window can't catch mouse movements. I tried to catch mouse movements in main dialog, but it failed, because focus is taken by popup window.
My goal is to close popup after any mouse movement.

You should post your code that 'fails'. It is hard to give specific advice when we have no information of what you are doing.
Have you looked at wxPopupWindow? http://docs.wxwidgets.org/trunk/classwx_popup_window.html
Personally, I find it easier to roll my own. Here's how the one I am working on right now looks
cNewDataPopup::cNewDataPopup( cPatDataset& data )
: wxDialog(NULL,-1,L"New data",wxPoint(200,200),wxSize(570,242),
wxDEFAULT_DIALOG_STYLE|wxSTAY_ON_TOP )
, myData( data )
{
...
Show();
}
To make this popup appear, simply call the constructor.
You would want to pass in your image to be displayed, store it in an attribute, handle the paint event by drawing your image on the client area.

Related

How to know if the mouse hovers over a widget in QT?

I am trying to implement in my program, that if the mouse hovers over my QChartView widget, that I get back the coordinates of the cursor.
I already tried, by installing an event filter on the widget
ui->chartView->setMouseTracking(true);
ui->chartView->installEventFilter(this);
and then writing the method for a mouse event
void MainWindow::mouseMoveEvent(QMouseEvent* event) {
qDebug() << event->pos();
}
but, i only get output when I click on the Mainwindow and hold the mousebutton which I clicked. When I click on the widget chartView I dont get any output.
I need to get output, when the mouse is hovering over the chartview
You don't need an event filter. You need to reimplement the methods QWidget::enterEvent() and QWidget::leaveEvent().
enterEvent() is called when the mouse enters the widget and leaveEvent() when it leaves it again.

Why the superclass'es OnPaint() method is called on pressing button?

I'm trying to implement the OnPaint() method in the subclass of the CButton MFC class.
class CImageButton : public CButton
public:
using CButton::CButton;
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CImageButton, CButton)
ON_WM_PAINT()
END_MESSAGE_MAP()
void CImageButton::OnPaint()
{ /* Draws an image; Doesn't call the CButton::OnPaint(). */ }
Then, I have a dialog. In the resource editor, I placed the button on its form. In the dialog declaration, I wrote
CImageButton m_btn;
and
DDX_Control(pdc, IDC_BUTTON, m_btn);
in the DoDataExchange(). So, I think, I done all in order to my painting routine be working. But I got the strange thing: when my dialog is opening the image is painted OK (without the button's border, as I implemented in my method). But, if I'm trying to click on it, the image disappears the button's border is drawn as well a text which I specified in the VS resource editor. I.e., it looks like the superclass'es OnPaint() method is called by some way. I even was placing a debugging output on the entry of my method -- it is really not called. Then, if I switch the focus to some other control, my button is being redrawn with image.
Please, don't suggest me to use other ways to draw the image on the button. I need to get an answer on the question asked.
Thank you!

Qt transfer mouse move events to new window

I'm handling mouse events (through an event filter) on a QTabBar to detect when the user clicks and drags a tab to tear it off. When this happens, I remove the current widget from the QTabWidget and create a new top level widget that I add it to so that it's detached and floating, just like when you tear off a tab in Chrome. This new floating window is a custom frameless widget I made that has a custom titlebar that I handle mouse events on to allow the user to drag the window around the desktop.
The problem I'm having is that when you click and drag the tab to pull it off and the new top level window is created, I can't seem to get the application to continue dragging the new window without the user clicking and dragging on my titlebar. I'd like for the original drag motion to just transfer to the new widget so that the use can keep dragging it until he releases the mouse button.
I've tried creating a "fake" QMouseEvent to pass to my title bar (by calling QCoreApplication::sendEvent(object, event) to make it think it's been clicked on, but it doesn't receive any mouse move events unless you actually click on it. I'm open to other ideas.
Update: I added some debugging statements and it looks like once I detach the tab and create the new floating window, the QMainWindow continues to receive the mouse move events until I release the mouse button. I'll try adding some code to forward these mouse events on to the new floating window, but that feels kinda hacky.
Correction: The QMainWindow is not receiving the mouse move events, an object named "MainWindowWindow" is, which is a QWidgetWindow that I guess is a private type used to manage top level windows?
Ok, I got it working, but I don't like it. As I said in my correction, once the tab is detached, the QWidgetWindow starts receiving the mouse move events. This is a private type that's not exposed via the API.
What I ended up doing was installing an event filter on the application and looking for mouse events on an object that inherits from QWidgetWindow. I then directly call new drag() and endDrag() methods that I added to my floating window class. In my eventFilter method, it looks like this.
if (watched->inherits("QWidgetWindow"))
{
if (floater) // <- the floating window that was recently detached
{
if (event->type() == QEvent::MouseMove)
{
floater->drag(QCursor::pos()); // <-- Pass global cursor pos
}
else if (event->type() == QEvent::MouseRelease)
{
floater->endDrag();
floater = nullptr;
}
}
}
Like I said, if feels dirty because I A) am checking for events on a private type that I'm not really supposed to know about, and B) telling another window to drag around when it has code to do this itself. But it works, and I've spent enough time working on this. If someone has a more elegant solution, I'm happy to hear it.

QT: shaded window effect (lights out)

I'm opening a modal window from my main window and my interest is to make the background dark so the top window is perfectly visible but the main one looks dark like in the "shade".
You can show some half-transparent widget over the mainwindow and it will create shadow effect.
For example, such widget.
class Overlay : public QWidget
{
public:
Overlay(QWidget *parent) {
setPalette(Qt::transparent);
setAttribute(Qt::WA_TransparentForMouseEvents);
}
protected:
void paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(QBrush(QColor(0,0,0, 150)));
painter.setPen(Qt::NoPen);
painter.drawRect(rect());
}
};
Then create this widget, resize and show:
overlay_.reset(new Overlay(this));
overlay_->resize(size());
overlay_->setVisible(true);
You can play with the shadow color and transperancy by changing brush in paintEvent.
Hope this is the effect you wished.
This is up to the window manager to add such effect.
For example, KWin and Mutter both have their way to handle dialogs. KWin does shade the main window, and I think Mutter does it too with some additional effect.
In Mac OS, modal window are already have special properties to put it in focus on relation of it's patent window.
The way windows handle this is by forcing the focus on the modal I think. But it really is the window manager's job, and up to the user's preference to choose what effect should be active.

Is it possible to catch mouse events in wxFrame

I have been searching the web and trying to find out if it is possible to catch mouse events in the actual wxFrame element in wxWidgets. Various sources say it is not possible and wxPanels should be used to capture events,is that true?
Thanks
Mouse events are not propagated upwards the window hierarchy, so if your frame is entirely covered by other windows, then it doesn't get any mouse events in the first place and hence you can't catch them there.
Of course, you can always handle any event from any other window in a method of wxFrame class using Bind(). For example:
MyFrame::MyFrame(...)
: wxFrame(...)
{
wxPanel* p = new wxPanel(this);
p->Bind(wxEVT_MOTION, &MyFrame::OnMouseMotion, this);
}
would allow you to handle mouse motion events happening over the panel in a frame method.
Mostly true.
Some mouse events go to the frame, like wxEVT_ENTER (or whatever it is called). Others go to the panel, e.g. wxEVT_RIGHT_UP. You can forward those events to the frame.
Bind to the event in both the panel and the frame.
Bind(wxEVT_RIGHT_UP, &MyFrame::OnMouse, ptr_to_myframe);
Bind(wxEVT_RIGHT_UP, &MyPanel::OnMouse, ptr_to_panel);
And then (assuming the frame is the parent of the panel),
void MyPanel::OnMouse(wxMouseEvent &event) {
wxPostEvent(GetParent(), event);
}
If the panel is scrolled, you probably want to forward an event with the unscrolled coordinates.
void MyScrolledPanel::OnMouse(wxMouseEvent &event) {
wxMouseEvent pevent(event);
CalcUnscrolledPosition(event.GetX(), event.GetY(), &pevent.m_x, &pevent.m_y);
wxPostEvent(GetParent(), pevent);
}