Drawing any rectangle to a GTK+ DrawingArea fills the whole DrawingArea - c++

I have a GTK+ DrawingArea that should display a rectangle in the top left corner. When I draw the rectangle using Cairo, the whole drawing area is filled with the color of the rectangle. How can I prevent that? Why does Cairo do that? What am I doing wrong?
#include <gtkmm.h>
class Window : public Gtk::Window
{
private:
Gtk::DrawingArea area;
bool on_area_expose(GdkEventExpose* event)
{
Gtk::Allocation allocation = area.get_allocation();
Cairo::RefPtr<Cairo::Context> context =
area.get_window()->create_cairo_context();
int width = allocation.get_width();
int height = allocation.get_height();
context->set_source_rgba(0, 0, 0, 1);
context->rectangle(0, 0, double(width)/10, double(height)/10);
context->paint();
return true;
}
public:
Window() : Gtk::Window()
{
area.signal_expose_event().connect(
sigc::mem_fun(*this, &Window::on_area_expose));
add(area);
}
};
int main(int argc, char* argv[])
{
Gtk::Main app(argc, argv);
Window window;
window.show_all();
Gtk::Main::run(window);
return 0;
}
I compiled the code using
g++ gtktest.cpp `pkg-config --libs --cflags gtkmm-2.4` -o gtktest

context->paint() paints the current source everywhere within the current clip region. The proper method to call is Gtk::Context::fill.

Related

Using QOpenGLWidget in a window with a custom title bar causes a black rectangle on some machines

The goal is to have an app on Windows using QMainWindow with a custom-drawn title bar and containing a QOpenGLWidget.
Using Qt::CustomizeWindowHint to make the title bar drawable was ruled out because of the issue described in QTBUG-47543 - a thick white frame along the top of the window:
So, falling back to platform-specific Win32 WM_NCCALCSIZE instead...
main.cpp:
#include <QApplication>
#include <QMainWindow>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <windowsx.h>
class MyGLWidget : public QOpenGLWidget {
Q_OBJECT
protected:
void initializeGL() override { QOpenGLContext::currentContext()->functions()->glClearColor(1.0f, 0.0f, 1.0f, 1.0f); }
};
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow() { setCentralWidget(new MyGLWidget); }
protected:
bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override
{
MSG* msg = reinterpret_cast<MSG*>(message);
switch (msg->message) {
case WM_NCCALCSIZE: {
NCCALCSIZE_PARAMS* params = (NCCALCSIZE_PARAMS*)(msg->lParam);
params->rgrc[0].right -= 8; // Sizes hard-coded for brevity
params->rgrc[0].left += 8;
params->rgrc[0].bottom -= 8;
*result = 0;
return true;
}
// case WM_NCHITTEST omitted for brevity
}
return false;
}
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
#include "main.moc"
TitleBar.pro:
QT += core gui openglwidgets widgets
SOURCES += main.cpp
On most machines, this gives the desired result:
The problem that's happening on a few machines is that a completely black rectangle is showing in the area where the standard title bar would normally be:
Replace the QOpenGLWidget with a regular QWidget and it looks correct on all machines.
Remove MainWindow::nativeEvent() and set Qt::CustomizeWindowHint on the window instead and the black rectangle problem goes away, but the above-mentioned thick white frame issue occurs instead.
Why would the black rectangle problem only happen on some machines and not others? (See Edit, below.)
Is there a way to use Qt::CustomizeWindowHint that avoids the white frame issue, without overriding nativeEvent()?
If not, is there a way to handle WM_NCCALCSIZE that works well with QOpenGLWidget on all machines?
Tests were done using Qt v6.2.0 on Windows 10 v21H1.
Edit:
The black rectangle goes away if QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL) is called at the top of main(), implying that the problem is specific to certain OpenGL drivers.

How to make a point appear using fltk?

After creating a window and drawing some shapes in it, I realized I cant make a point and just appear it on the window. I've searched the manual but I cant make anything out of it. Im using the fltk 1.3.0. How can I do it ?
Fltk comes with a bunch of example projects that are helpful. If you look at the line_style example you can easily reduce it to something drawing points like this:
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/fl_draw.H>
class TestWindow : public Fl_Window {
void draw()
{
fl_color(255, 0, 0);
fl_begin_points();
fl_point(50, 50);
fl_point(51, 51);
fl_end_points();
}
public:
TestWindow(int w, int h, const char *l = 0)
: Fl_Window(w, h, l) {}
};
int main(int argc, char ** argv) {
Fl_Window *window = new TestWindow(200, 200);
window->end();
window->show(argc, argv);
return Fl::run();
}
But just as a word of advice, drawing single points directly onto the window is rarely the smart thing to do. Drawing into images/buffers and then displaying them is the better alternative most of the time.
edit:
here's is an example of putting the drawing code in the main function.
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/fl_draw.H>
class TestWindow : public Fl_Window {
void draw() {}
public:
TestWindow(int w, int h, const char *l = 0) : Fl_Window(w, h, l) {}
};
int main(int argc, char ** argv) {
Fl_Window *window = new TestWindow(200, 200);
window->end();
window->show(argc, argv);
window->make_current();
fl_color(255, 0, 0);
fl_begin_points();
fl_point(50, 50);
fl_point(51, 51);
fl_end_points();
return Fl::run();
}
You should take notice of the disclaimer for make_current in the manual
Danger: incremental update is very hard to debug and maintain!
None of this is good practise beyond using it for simple exercises.
Based on the previous answer to this question, I found this in the documentation:
fl_begin_points()
Starts drawing a list of points.
Points are added to the list with fl_vertex()
So this is some code that shows some points (I added more to really see the points):
#include <FL/fl_draw.H>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
class Drawing : public Fl_Window {
void draw(){
fl_begin_points();
//adding cushion to points to be able to see them.
//center at 10,10
fl_vertex(9,9);
fl_vertex(9,10);
fl_vertex(9,11);
fl_vertex(10,9);
fl_vertex(10,10);
fl_vertex(10,11);
fl_vertex(11,9);
fl_vertex(11,10);
fl_vertex(11,11);
fl_end_points();
fl_color(FL_BLACK);
}
public:
Drawing(int w, int h, const char *l = 0) : Fl_Window(w, h, l){}
};
int main(int argc, char **argv){
Fl_Window *window = new Drawing(340,180);
window->end();
window->show(argc, argv);
return Fl::run();
}

How to disable minimum size setting on widgets for drawing a window background image

I'm new to Gtkmm3 and I try several things. At the moment, I program a window class with a background image. I have searched a lot, but I don't find any useful/helpful examples for Gtkmm3. Finally, it should be possible to resize the window, and the background resizes itself by building a pattern of the same image and cutting them to the desired size (not by scaling the image).
So I add a base grid (Gtk::Grid) to the main window (Gtk::Image), load an image, put them into a background grid (Gtk::Grid) and attach this to the base grid at the same position as the foreground grid (Gtk::Grid).
A method helps to resize the background.
Here comes the code:
makefile
SRC = main.cpp
SRC += ./src/MainWindow.cpp
TARGET = prog
CC = g++
LIBS = `pkg-config gtkmm-3.0 --cflags --libs`
all:
$(CC) $(SRC) $(LIBS) -o $(TARGET)
main.cpp
#include "./inc/MainWindow.h"
#include <gtkmm.h>
int main (int argc, char* argv[])
{
Glib::RefPtr<Gtk::Application> app =
Gtk::Application::create(argc, argv, "org.gtkmm.example");
MainWindow mainWindow;
//Shows the mainWindow and returns when it is closed.
return app->run(mainWindow);
}
inc/MainWindow.h
#ifndef MAINWINDOW_H_INCLUDED
#define MAINWINDOW_H_INCLUDED
#include <gtkmm.h>
#include <gdkmm.h>
class MainWindow :
public Gtk::Window
{
public:
/** Default constructor */
MainWindow();
/** Default destructor */
virtual ~MainWindow();
protected:
private:
// signal handlers
void on_ButtonQuit_clicked ( void );
void on_MainWindow_check_resize();
// methods
void resizeBackground(int width, int height);
// member data
Gtk::Grid m_GridBase;
Gtk::Grid m_ForegroundGrid;
Gtk::Label m_Label1;
Gtk::ButtonBox m_ButtonBox;
Gtk::Button m_ButtonQuit;
Gtk::Image m_BackgroundImage;
Glib::RefPtr<Gdk::Pixbuf> m_BackgroundImagePixbuf;
int m_BackgroundImageWidth;
int m_BackgroundImageHeight;
bool m_resizeBackgroundStatus;
};
#endif // MAINWINDOW_H_INCLUDED
src/MainWindow.cpp
#include "../inc/MainWindow.h"
MainWindow::MainWindow() :
m_ButtonBox(),
m_ButtonQuit( Gtk::Stock::QUIT ),
m_BackgroundImage( "res/background.png" ),
m_ForegroundGrid(),
m_GridBase(),
m_Label1 ( "Beispiel Label",
/*xalign*/ Gtk::ALIGN_CENTER,
/*yalign*/ Gtk::ALIGN_CENTER,
/*mnemonic*/ false)
{
// setting up the window
this->set_title( "Window with backround" );
this->set_border_width(0);
// hide titlebar
//this->set_decorated(false);
m_BackgroundImagePixbuf = m_BackgroundImage.get_pixbuf();
m_BackgroundImageWidth = m_BackgroundImagePixbuf->get_width();
m_BackgroundImageHeight = m_BackgroundImagePixbuf->get_height();
// ad the base grid to the window
this->add(m_GridBase);
// attach the foreground grid and the background image to the base grid
m_GridBase.attach(m_ForegroundGrid,1,1,1,1);
// at least (!) attach the background picture
m_GridBase.attach(m_BackgroundImage,1,1,1,1);
// pack the foreground grid
m_ForegroundGrid.attach(m_ButtonBox,2,2,1,1);
m_ForegroundGrid.attach(m_Label1,1,1,1,1);
m_ButtonBox.pack_start(m_ButtonQuit, Gtk::PACK_SHRINK);
// get the dimension of packed foreground grid for resizing the background
m_ForegroundGrid.show_all();
int min_width; int nat_width; int min_height; int nat_height;
m_ForegroundGrid.get_preferred_width(min_width,nat_width);
m_ForegroundGrid.get_preferred_height(min_height,nat_height);
this->resizeBackground(min_width, min_height);
// initialize the status flag for on_MainWindow_check_resize
m_resizeBackgroundStatus = true;
// connect signals
m_ButtonQuit.signal_clicked().connect(
sigc::mem_fun(*this, &MainWindow::on_ButtonQuit_clicked));
this->signal_check_resize().connect(
sigc::mem_fun(*this, &MainWindow::on_MainWindow_check_resize));
this->show_all_children();
}
MainWindow::~MainWindow()
{
//dtor
}
void MainWindow::on_ButtonQuit_clicked(void)
{
this->hide();
}
void MainWindow::on_MainWindow_check_resize()
{
// check status to prevent recursive callings
if(m_resizeBackgroundStatus==true)
{
Gtk::Allocation allocation = this->get_allocation();
const int width = allocation.get_width();
const int height = allocation.get_height();
m_resizeBackgroundStatus = false;
this->resizeBackground(width, height);
}
else
{
m_resizeBackgroundStatus = true;
}
}
void MainWindow::resizeBackground(int width, int height)
{
Glib::RefPtr<Gdk::Pixbuf> BackgroundPixbuf =
Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB,true,8,width,height);
int xcnt = width/m_BackgroundImageWidth + 1;
int ycnt = height/m_BackgroundImageHeight + 1;
Glib::RefPtr<Gdk::Pixbuf> BackgroundTmpPixbuf =
Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB,true,8,
xcnt*m_BackgroundImageWidth,
ycnt*m_BackgroundImageHeight);
for( xcnt = width/m_BackgroundImageWidth; xcnt>=0; xcnt--)
{
for( ycnt = height/m_BackgroundImageHeight; ycnt>=0; ycnt--)
{
m_BackgroundImagePixbuf->scale(
BackgroundTmpPixbuf, // destination Pixbuf
xcnt*m_BackgroundImageWidth, // dest_x
ycnt*m_BackgroundImageHeight, // dest_y
m_BackgroundImageWidth, // dest_width
m_BackgroundImageHeight, // dest_height
xcnt*m_BackgroundImageWidth, // offset_x = dest_x
ycnt*m_BackgroundImageHeight, // offset_y = dest_y
1, // scale_x
1, // scale_y
Gdk::INTERP_NEAREST //interp_type
);
}
}
BackgroundTmpPixbuf->scale(
BackgroundPixbuf, // destination Pixbuf
0, // dest_x
0, // dest_y
width, // dest_width
height, // dest_height
0, // offset_x = dest_x
0, // offset_y = dest_y
1, // scale_x
1, // scale_y
Gdk::INTERP_NEAREST //interp_type
);
m_BackgroundImage.set(BackgroundPixbuf);
// int min_width; int nat_width; int min_height; int nat_height;
// m_Grid.get_preferred_width(min_width,nat_width);
// m_Grid.get_preferred_height(min_height,nat_height);
// printf("%d\n",min_width);
// m_GridBase.set_size_request(min_width,min_height);
// this->set_size_request(min_width,min_height);
// m_BackgroundImage.set_size_request(min_width,min_height);
// const Gdk::Geometry geometry = 0;
// this->set_geometry_hints(geometry,0);
}
res/background.png
snapshot of the window after start
snapshot of the window after resizing
The Question
As you can see, you can enlarge the window and the background fits its size. But you can't reduce the size. What do I have to do to enable resizing back to the size of the foreground?
I search for the opposite method of get_preferred_width because I think this adjustment limits resizing.
My Approaches
I tried out set_size_request or set_geometry_hints, but without any effect.

Mouse events QT

I want to to allow user to select a region with mouse, like you can do mostly everywhere.For more clarity just imagine your desktop on Windows, and click the left button and move the mouse with the button holed. The following will happen: you will see how the region that your mouse passed is highlighted with a rectangle. That is exactly what I want to do.
p.s. Mathematically I know how to calculate, and also know how to draw the rectangle by being able to track mouse position when it is pressed.
Q1: How to track mouse position?
Q2: Any alternative way to do what I want?
The simplest way is to use the Graphics View Framework. It provides mechanism for item selection, display of a rubber band rectangle, detection of intersection of the rubber band with the items, etc. Below is a self contained example. It lets you select and drag multiple items using either Ctrl/Cmd-click to toggle selection, or rubber banding.
OpenGL is used to render the background, and you can put arbitrary OpenGL content there.
main.cpp
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGLWidget>
static qreal rnd(qreal max) { return (qrand() / static_cast<qreal>(RAND_MAX)) * max; }
class View : public QGraphicsView {
public:
View(QGraphicsScene *scene, QWidget *parent = 0) : QGraphicsView(scene, parent) {
setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
}
void drawBackground(QPainter *, const QRectF &) {
QColor bg(Qt::blue);
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
};
void setupScene(QGraphicsScene &s)
{
for (int i = 0; i < 10; i++) {
qreal x = rnd(1), y = rnd(1);
QAbstractGraphicsShapeItem * item = new QGraphicsRectItem(x, y, rnd(1-x), rnd(1-y));
item->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
item->setPen(QPen(Qt::red, 0));
item->setBrush(Qt::lightGray);
s.addItem(item);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene s;
setupScene(s);
View v(&s);
v.fitInView(0, 0, 1, 1);
v.show();
v.setDragMode(QGraphicsView::RubberBandDrag);
v.setRenderHint(QPainter::Antialiasing);
return a.exec();
}

Segfault when creating smartpointer on CairoContext

I got some problems when creating a Cairo::RefPtr on a Cairo-Context.
I really can't imagine why this segfaults, except the pointer ist pointing on something completely wrong.
This is my code.
int main(int argc, char * argv[])
{
Gtk::Main kit(argc, argv);
Gtk::Window window;
Gtk::DrawingArea drawarea;
window.add(drawarea);
Cairo::RefPtr<Cairo::Context> ccontext = drawarea.get_window()->create_cairo_context();
Gtk::Allocation allocation = drawarea.get_allocation();
const int width = allocation.get_width();
const int height = allocation.get_height();
ccontext->set_source_rgb(1.0, 0.0, 0.0);
ccontext->set_line_width(2.0);
ccontext->move_to(0,0);
ccontext->line_to(width, height);
Gtk::Main::run(window);
}
And this is what GDB says:
Starting program: /home/marian/Desktop/C++/Langton/Langton [Thread
debugging using libthread_db enabled]
Program received signal SIGSEGV, Segmentation fault. 0xb7be852e in
Gdk::Window::create_cairo_context() () from /usr/lib/libgdkmm-3.0.so.1
I compiled this with gcc (GCC) 4.6.1 20110819 (prerelease).
Thanks in advance
Gtk::Widget::get_window() returns a null Glib::RefPtr, since the widget has not been realized just yet.
Based on the GtkDrawingArea documentation, you need to hook onto the "draw" signal to handle drawing, where your Cairo context is already created and handed to you. Going back to the Gtkmm reference, you would use Gtk::Widget::signal_draw() to hook onto that, or you could overload the virtual on_draw() function to handle your drawing.
Additionally, you also need to call .show() on each widget, i.e. your DrawingArea and your Window, and call ccontext->stroke() to get the line actually drawn.
The result would look something like:
#include <gtkmm.h>
bool draw (const Cairo::RefPtr<Cairo::Context> &ccontext, Gtk::DrawingArea *drawarea)
{
Gtk::Allocation allocation = drawarea->get_allocation();
const int width = allocation.get_width();
const int height = allocation.get_height();
ccontext->set_source_rgb(1.0, 0.0, 0.0);
ccontext->set_line_width(2.0);
ccontext->move_to(0,0);
ccontext->line_to(width, height);
ccontext->stroke ();
return true;
}
int main(int argc, char * argv[])
{
Gtk::Main kit(argc, argv);
Gtk::Window window;
Gtk::DrawingArea drawarea;
drawarea.signal_draw ().connect (sigc::bind (sigc::ptr_fun (&draw),
&drawarea));
window.add(drawarea);
window.show_all ();
Gtk::Main::run(window);
return 0;
}
or alternatively:
#include <gtkmm.h>
class LineBox : public Gtk::DrawingArea
{
protected:
virtual bool on_draw (const Cairo::RefPtr<Cairo::Context> &ccontext);
};
bool LineBox::on_draw (const Cairo::RefPtr<Cairo::Context> &ccontext)
{
Gtk::Allocation allocation = get_allocation();
const int width = allocation.get_width();
const int height = allocation.get_height();
ccontext->set_source_rgb(1.0, 0.0, 0.0);
ccontext->set_line_width(2.0);
ccontext->move_to(0,0);
ccontext->line_to(width, height);
ccontext->stroke ();
return true;
}
int main(int argc, char * argv[])
{
Gtk::Main kit(argc, argv);
Gtk::Window window;
LineBox drawarea;
window.add(drawarea);
window.show_all ();
Gtk::Main::run(window);
return 0;
}