Cet article fait parti d’un ensemble d’exemples et d’articles sur gtkmm
consultables ici. Cet exemple montre comment afficher une image (Pixbuf) dans une zone de dessin Cairo. L’image peut être déplacée, redimensionnée, centrée ou ajustée avec la souris. Le redimensionnement est réalisé avec la molette. Les déplacements sont font avec le clic gauche. Le bouton droit permet d’afficher un menu contextuel (popup) avec différentes options illustrées ci-dessous.

Téléchargement
Code source
main.cpp
#include
#include
int main(int argc, char* argv[])
{
// Initialize gtkmm and create the main window
Glib::RefPtr app = Gtk::Application::create(argc, argv, "www.lucidarme.me");
Gtk::Window window;
// Create the drawing and add an image from file
ScrollImage Img;
Img.set_image_from_file("gtk.png");
// Insert the drawing in the window
window.add(Img);
// Resize the window
window.resize(600,400);
// Set the window title
window.set_title("ScrollImage");
// Show the drawing
Img.show();
// Start main loop
return app->run(window);
}
scrollimage.h
#ifndef SCROLLIMAGE_H
#define SCROLLIMAGE_H
#include
#include
// Minimun scale (low boundary)
#define MIN_SCALE 0.05
class ScrollImage : public Gtk::DrawingArea
{
public:
/*!
* \brief ScrollImage Constructor, initialize the widget.
*/
ScrollImage();
/*!
* \brief set_image_from_file Load the image to display.
* \param filename File name of the image.
*/
void set_image_from_file(const std::string& filename);
protected:
/*!
* \brief on_draw Override the draw handler : update display.
* Don't call this function to update display, call queue_draw().
* \param cr Cairo context.
* \return Always false.
*/
virtual bool on_draw(const Cairo::RefPtr& cr);
/*!
* \brief displayTarget Display a little target at coordinates x,y.
* \param cr Cairo context.
* \param x x-coordinate of the center of the target.
* \param y y-coordinate of the center of the target.
*/
void displayTarget(const Cairo::RefPtr& cr,double x, double y);
/*!
* \brief on_scroll_event This function is called when the mouse scroll changes.
* This is a default handler for the signal signal_scroll_event().
* \param event Properties of the event.
* \return Always true (the event is processed).
*/
bool on_scroll_event(GdkEventScroll *event);
/*!
* \brief on_button_press_event This function is called when a mouse button is pressed.
* This is a default handler for the signal signal_button_press_event().
* \param event Properties of the event.
* \return True if the event is processed, false otherwise.
*/
bool on_button_press_event(GdkEventButton *event);
/*!
* \brief on_button_release_event This function is called when a mouse button is released
* This is a default handler for the signal signal_button_release_event().
* \param event Properties of the event.
* \return True if the event is processed, false otherwise.
*/
bool on_button_release_event(GdkEventButton *event);
/*!
* \brief on_motion_notify_event This function is called when the pointer is moved
* This is a default handler for the signal signal_motion_notify_event().
* \param event Properties of the event.
* \return True if the event is processed, false otherwise.
*/
bool on_motion_notify_event(GdkEventMotion*event);
protected:
/*!
* \brief resetView Reset the view (best image fit in the window).
* \param winWidth Width of the window (in pixels).
* \param winHeight Height of the window (in pixels).
* \param imgWidth Width of the image (in pixels).
* \param imgHeight Height of the image (in pixels).
*/
void fitImage(int winWidth,int winHeight, int imgWidth, int imgHeight);
/*!
* \brief fit Set the fit flag to one and update display
* Next call of on_draw will fit the image according to the drawing area.
*/
void fit();
/*!
* \brief reset Reset display (center the image and set scale to 1:1) and update display
*/
void reset();
/*!
* \brief resetScale Set scale to 1:1 and update display
*/
void resetScale();
/*!
* \brief setCenter Center the image on the pointer position from last click
*/
void setCenter();
/*!
* \brief showHideTarget Show or hide the center target and update display
*/
void showHideTarget();
private:
// Image to display
Glib::RefPtr image;
// Scale of the image
double scale;
// Coordinates of the image point to place at the center of the window (focussed pixel)
double imgFocusX,imgFocusY;
// Used to memorize last mouse coordinates
int lastXMouse, lastYMouse;
// Flags
bool resetFlag;
bool moveFlag;
bool targetFlag;
// Popup menu and submenus
// Main popup menu
Gtk::Menu m_Menu_Popup;
Gtk::MenuItem MenuItemFit;
Gtk::MenuItem MenuItemRstView;
Gtk::MenuItem MenuItemRstScale;
Gtk::MenuItem MenuItemSetCenter;
Gtk::MenuItem MenuItemTarget;
Gtk::SeparatorMenuItem MenuItemLine1;
Gtk::SeparatorMenuItem MenuItemLine2;
};
#endif // SCROLLIMAGE_H
scrollimage.cpp
#include "scrollimage.h"
// Constructor
ScrollImage::ScrollImage()
{
// Set masks for mouse events
add_events(Gdk::BUTTON_PRESS_MASK |
Gdk::BUTTON_RELEASE_MASK |
Gdk::SCROLL_MASK |
Gdk::SMOOTH_SCROLL_MASK |
Gdk::POINTER_MOTION_MASK);
// ::: Create popup menu :::
// Add and connect action set pointer as center
MenuItemSetCenter.set_label("Set center here");
MenuItemSetCenter.signal_activate().connect(sigc::mem_fun(*this,&ScrollImage::setCenter));
m_Menu_Popup.append(MenuItemSetCenter);
// Add a separator
m_Menu_Popup.append(MenuItemLine1);
// Add and connect action Fit image to drawing area
MenuItemFit.set_label("Fit image");
MenuItemFit.signal_activate().connect(sigc::mem_fun(*this,&ScrollImage::fit));
m_Menu_Popup.append(MenuItemFit);
// Add and connect action reset (center and set scale to 1)
MenuItemRstView.set_label("Reset view");
MenuItemRstView.signal_activate().connect(sigc::mem_fun(*this,&ScrollImage::reset));
m_Menu_Popup.append(MenuItemRstView);
// Add and connect action reset scale to 1
MenuItemRstScale.set_label("Reset scale");
MenuItemRstScale.signal_activate().connect(sigc::mem_fun(*this,&ScrollImage::resetScale));
m_Menu_Popup.append(MenuItemRstScale);
// Add a separator
m_Menu_Popup.append(MenuItemLine2);
// Add and connect action reset scale to 1
MenuItemTarget.set_label("Hide center target");
MenuItemTarget.signal_activate().connect(sigc::mem_fun(*this,&ScrollImage::showHideTarget));
m_Menu_Popup.append(MenuItemTarget);
// Show the menu
m_Menu_Popup.show_all();
// Connect the menu to this Widget
m_Menu_Popup.accelerate(*this);
// ::: Initialize view and flags :::
fit();
moveFlag=false;
targetFlag=true;
}
// Load an image from file
void ScrollImage::set_image_from_file(const std::string& filename)
{
image = Gdk::Pixbuf::create_from_file(filename);
}
// Mouse wheel event detected : update scale
bool ScrollImage::on_scroll_event(GdkEventScroll *event)
{
// Compute the new scale according to mouse scroll
double newScale=scale*(1-event->delta_y/20);
if (newScalex - get_allocated_width()/2.;
double DeltaY=event->y - get_allocated_height()/2.;
imgFocusX=imgFocusX + DeltaX/scale - DeltaX/newScale ;
imgFocusY=imgFocusY + DeltaY/scale - DeltaY/newScale ;;
// Update scale and redraw the widget
scale=newScale;
queue_draw();
// Event has been handled
return true;
}
// Mouse button pressed : process mouse button event
bool ScrollImage::on_button_press_event(GdkEventButton *event)
{
// Check if the event is a left button click.
if (event->button == 1)
{
// Memorize pointer position
lastXMouse=event->x;
lastYMouse=event->y;
// Start moving the view
moveFlag=true;
// Event has been handled
return true;
}
// Check if the event is a right button click.
if(event->button == 3)
{
// Memorize mouse coordinates
lastXMouse=event->x;
lastYMouse=event->y;
// Display the popup menu
m_Menu_Popup.popup(event->button, event->time);
// The event has been handled.
return true;
}
// Event has not been handled
return false;
}
// Mouse button released : process mouse button event
bool ScrollImage::on_button_release_event(GdkEventButton *event)
{
// Check if it is the left button
if (event->button==1 && moveFlag)
{
// End of motion
moveFlag=false;
// Update image focus
imgFocusX -= (event->x-lastXMouse)/scale;
imgFocusY -= (event->y-lastYMouse)/scale;
// Update display
queue_draw();
return true;
}
// Event has been handled
return false;
}
// Mouse pointer moved : process event
bool ScrollImage::on_motion_notify_event (GdkEventMotion*event)
{
// If the left button is pressed, move the view
if (moveFlag)
{
// Get mouse coordinates
int XMouse=event->x;
int YMouse=event->y;
// Update image focus
imgFocusX -= (XMouse-lastXMouse)/scale;
imgFocusY -= (YMouse-lastYMouse)/scale;
// Memorize new position of the pointer
lastXMouse=XMouse;
lastYMouse=YMouse;
// Update view
queue_draw();
return true;
}
// Event has been handled
return false;
}
// Reset view (scale is set to 1:1 and image is centered)
void ScrollImage::reset()
{
// Set scale to 1:1
scale=1;
// Update image focus
imgFocusX = image->get_width()/2.;
imgFocusY = image->get_height()/2.;
// Update display
queue_draw();
}
// Reset scale to 1:1
void ScrollImage::resetScale()
{
scale=1;
// Update display
queue_draw();
}
// Center the dispay at the pointer coordinates
void ScrollImage::setCenter()
{
// Update image focus
imgFocusX += (lastXMouse - get_allocated_width()/2.)/scale;
imgFocusY += (lastYMouse - get_allocated_height()/2.)/scale;
// Update display
queue_draw();
}
// Best fit of the image in the display
void ScrollImage::fit()
{
// Set reset flag to true, next call of on_draw will reset display
resetFlag=true;
queue_draw();
}
// Hide or show the target center
void ScrollImage::showHideTarget()
{
// Invert flag
targetFlag=!targetFlag;
// Update popup menu according to current status
if (targetFlag)
MenuItemTarget.set_label("Hide center target");
else
MenuItemTarget.set_label("Show center target");
// Update display
queue_draw();
}
// Reset view to fit in the drawing area
void ScrollImage::fitImage(int winWidth,int winHeight, int imgWidth, int imgHeight)
{
// Compute ratio of the window and the image
double winRatio=(double)winWidth/winHeight;
double imgRatio=(double)imgWidth/imgHeight;
// Check what is the best fit for the image according to the ratio
if (imgRatio& cr)
{
// Get the window size
int winWidth=get_allocated_width();
int winHeight=get_allocated_height();
// Get the image size
int imgWidth=image->get_width();
int imgHeight=image->get_height();
// If requested, reset view
if (resetFlag) fitImage(winWidth,winHeight,imgWidth,imgHeight);
// Create a new image for display filled with grey
Glib::RefPtr display = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB,false,8,winWidth,winHeight);
display->fill(0x5F5F5F00);
// Compute offset of the source image
double OffsetX=winWidth/2-imgFocusX*scale;
double OffsetY=winHeight/2-imgFocusY*scale;
// Compute top left coordinate of the image in the area
double Min_X=std::max(0.,OffsetX);
double Min_Y=std::max(0.,OffsetY);
// Compute bottom right coordinates of the image in the area
double Max_X=std::min((double)winWidth,winWidth/2+(imgWidth-imgFocusX)*scale);
double Max_Y=std::min((double)winHeight,winHeight/2+(imgHeight-imgFocusY)*scale);
// Compute width and height
double Width=Max_X-Min_X;
double Height=Max_Y-Min_Y;
// Scale image
image->scale(display,
Min_X,Min_Y,
Width,Height,
OffsetX,OffsetY,
scale,scale,Gdk::INTERP_TILES);
// Display the image in the drawing area
Gdk::Cairo::set_source_pixbuf(cr,display,0,0);
cr->rectangle(0,0,winWidth,winHeight);
cr->fill();
// Display the center target if requested
if (targetFlag) displayTarget(cr,winWidth/2.,winHeight/2.);
// Event has been handled
return false;
}
// Display a target at coordinates x,y
void ScrollImage::displayTarget(const Cairo::RefPtr& cr,double x, double y)
{
// Set color to black
cr->set_source_rgba(0,0,0,0.5);
// Display black quaters
cr->arc(x,y,15,0,M_PI/2.);
cr->line_to(x,y);
cr->fill();
cr->arc(x,y,15,M_PI,3.*M_PI/2.);
cr->line_to(x,y);
cr->fill();
// Set color to white
cr->set_source_rgba(1,1,1,0.5);
// Display white quaters
cr->arc(x,y,15,M_PI/2.,M_PI);
cr->line_to(x,y);
cr->fill();
cr->arc(x,y,15,3.*M_PI/2.,0);
cr->line_to(x,y);
cr->fill();
// Set color to black
cr->set_source_rgba(0,0,0,0.8);
// Display the cross
cr->move_to(x-20,y);
cr->line_to(x+20,y);
cr->stroke();
cr->move_to(x,y-20);
cr->line_to(x,y+20);
cr->stroke();
// Display the circle
cr->arc(x,y,15,0,2*M_PI);
cr->stroke();
}