Call methods from classes from a vector of different classes - c++

I'm using raylib to create some simple GUI elements, like Labels.
Each class has a void draw() method. For each widget, draw() needs to be called in the window mainloop. This leads to numerous draw() calls.
Instead of this, I'd like to define a DrawCalls class. This would have a std::vector containing each of the widget's draw().
Ideally widgets would be added like this
Drawcalls d;
Label foo("This is a Label", xLocation, yLocation, fontSize, font, colour);
d.addWidget(foo);
Then d.calls() would be called in the window's mainloop.
I've tried to pass the draw method as a function pointer and add it to a std::vector<void*> and then loop through the vector calling each void. Although this didn't work.
This is my code so far:
class Widget {
public:
void draw();
};
class DrawCalls {
private:
std::vector<Widget>
}
class Label : public Widget{
public:
Label(std::string text, float x, float y, float fontsize, Font font, Color colour = WHITE) {
this->text = text;
this->x = x;
this->y = y;
this->fontsize = fontsize;
this->font = font;
this->colour = colour;
}
void changeText(std::string newText) {
this->text = newText;
}
void setXY(float x, float y) {
this->x = x;
this->y = y;
}
void draw(bool draw) {
if (draw) {
DrawTextEx(this->font, this->text.c_str(), (Vector2){x,y}, this->fontsize, 0, this->colour);
}
}
protected:
std::string text;
float x, y;
float fontsize;
Font font;
Color colour;
};
class LabelWithBackground : protected Label {
public:
LabelWithBackground(std::string text, float x, float y, float fontsize, Font font, Color colour = WHITE, Color backgroundcolour = BLACK, Monopoly::padding padding = (Monopoly::padding){10, 10, 10, 10})
: Label(text, x, y, fontsize, font, colour) // Inherit from Label
{
this->backgroundcolour = backgroundcolour;
this->padding = padding;
Label::setXY((Label::x + padding.left), (Label::y + padding.top)); // Shift text over
}
void changeText(std::string newText) {
Label::changeText(newText);
}
void setXY(float x, float y) {
Label::setXY(x, y);
}
void draw(bool draw) {
if (draw) {
Vector2 measured = MeasureTextEx(Label::font, Label::text.c_str(), Label::fontsize, 0);
DrawRectangle(Label::x - this->padding.left, Label::y - this->padding.top, measured.x + this->padding.left + this->padding.right, measured.y + this->padding.top + this->padding.bottom, this->backgroundcolour);
Label::draw(true);
}
}
private:
Color backgroundcolour;
Monopoly::padding padding;
};
Thanks for any help pointing me in the right direction.

Related

Class variable gets updated only once in gameloop

I have a problem with updating the value of a class variable on each frame of gameloop.
I am using wxWidgets for creating a cross-platform window and for graphics as well as gameloop. This is my main Window class which implements rendering and the gameloop.
#include <wx/wx.h>
#include "window.h"
#include "../entity/entity.h"
#include "../../configuration.h"
Window::Window(const wxString & title, const wxPoint & position, const wxSize & size): wxFrame(nullptr, wxID_ANY, title, position, size) {
timer = new wxTimer(this, 1);
Entity entity((width / 2) - 50, 100, 100, 100, wxColour(255, 0, 0));
AddEntity(entity);
Connect(wxEVT_PAINT, wxPaintEventHandler(Window::OnPaint));
Connect(wxEVT_TIMER, wxCommandEventHandler(Window::OnTimer));
Start();
}
void Window::Render(wxPaintEvent & event) {
wxPaintDC dc(this);
for (Entity entity : entities) {
entity.Render(dc);
}
}
void Window::Update(wxCommandEvent & event) {
for (Entity entity : entities) {
entity.Update();
}
}
void Window::AddEntity(Entity & entity) {
entities.push_back(entity);
}
void Window::OnTimer(wxCommandEvent & event) {
Update(event);
}
void Window::OnPaint(wxPaintEvent & event) {
Render(event);
}
void Window::Start() {
isStarted = true;
timer->Start(10);
}
void Window::Stop() {
isPaused = !isPaused;
if (isPaused) {
timer->Stop();
} else {
timer->Start(10);
}
}
Here is the Entity class which represent a rectangle that can be drawn onto the window.
#include <wx/wx.h>
#include <stdlib.h>
#include "entity.h"
#include "../gravity/gravity.h"
Entity::Entity(int _x, int _y, int _width, int _height, wxColour _color) : x( _x ), y( _y ), width( _width ), height( _height ), color( _color ) {
}
void Entity::Render(wxPaintDC & dc) {
wxPen pen(color);
dc.SetPen(pen);
dc.DrawLine(x, y, x + width, y);
dc.DrawLine(x, y + height, x + width, y + height);
dc.DrawLine(x, y, x, y + height);
dc.DrawLine(x + width, y, x + width, y + height);
}
void Entity::Update() {
y += 1;
std::cout << y << std::endl;
}
On each call of the Entity::Update() method, I want to increment the y position and rerender the entity. However, the value of y gets incremented only once and stays the same for the rest of the application lifetime and I can't seem to understand why. I'll be thankful for any help.
When you loop over your entities like this (in Window::Render, Window::Update):
for (Entity entity : entities) {
in each iteration entity will get a copy of the element in entities.
In order to operate on your actual entities via a reference you need to change it to:
//----------v---------------------
for (Entity & entity : entities) {
Another option is to use auto & (auto by itself does not include "reference-ness" and so will force again a copy as in your code):
//---vvvvvv---------------------
for (auto & entity : entities) {
Note that even if you only read data from your elements (and don't modify it) you can change your loop to use const & to save the copies.

Gtkmm : Drawing with cairo

Using Gtkmm and Cairo, I want to be able to draw different shapes on photos. In the header bar of my window, I have two buttons representing shapes to draw (circle and rectangle). When you click one of them, you can draw its associated shape. Here is mt code:
MyWindow.cpp
#include "MyWindow.h"
MyWindow::MyWindow()
: circleButton("circle"),
rectangleButton("rectangle ") {
set_default_size(700, 700);
set_position(Gtk::WIN_POS_CENTER);
header.set_show_close_button(true);
header.pack_start(rectangleButton);
header.pack_start(circleButton);;
set_titlebar(header);
// Dwg is an instance of Drawing class
circleButton.signal_clicked().connect([&] {
Dwg.switch_to_circle();
});
rectangleButton.signal_clicked().connect([&] {
Dwg.switch_to_rectangle();
});
add(Dwg);
show_all();
}
Drawing.h
#ifndef DRAWING_H
#define DRAWING_H
#include <gtkmm.h>
#include <cairo/cairo.h>
class MyDrawing : public Gtk::Layout {
public:
MyDrawing();
~MyDrawing();
void switch_to_circle();
void switch_to_rectangle();
protected:
virtual bool draw_image(const Cairo::RefPtr<::Cairo::Context> &cr);
virtual bool draw_rectangle(const Cairo::RefPtr<::Cairo::Context> &cr);
virtual bool draw_circle(const Cairo::RefPtr<::Cairo::Context> &cr);
private:
Glib::RefPtr<Gdk::Pixbuf> pix;
double beginPoint_x, beginPoint_y, endPoint_x, endPoint_y, lineWidth,width,height;
bool isDrawRectangle;
};
#endif // DRAWING_H
Drawing.cpp
#include <iostream>
#include "MyDrawing.h"
#include <cairomm/context.h>
#include <cairomm/surface.h>
MyDrawing::MyDrawing()
: isDrawRectangle(true),
width(20),
height(20) {
pix = Gdk::Pixbuf::create_from_file("file.svg", 500, 500);
if (pix) {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_image));
}
add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON_PRESS_MASK);
signal_button_press_event().connect([&](GdkEventButton *e) {
this->beginPoint_x = e->x;
this->beginPoint_y = e->y;
if(isDrawRectangle) {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_rectangle));
queue_draw();
}
else {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_circle));
queue_draw();
}
return true;
});
signal_motion_notify_event().connect([&](GdkEventMotion *e) {
this->endPoint_x = e->x;
this->endPoint_y = e->y;
width = endPoint_x - beginPoint_x;
height = endPoint_y - beginPoint_y;
if(isDrawRectangle) {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_rectangle));
queue_draw();
}
else {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_circle));
queue_draw();
}
return true;
});
}
MyDrawing::~MyDrawing() = default;
bool MyDrawing::draw_image(const Cairo::RefPtr<::Cairo::Context> &cr) {
std::cout << "signal img" << std::endl;
if (pix) {
cr->save();
Gdk::Cairo::set_source_pixbuf(cr, pix, 100, 100);
cr->rectangle(0, 0, get_width(), get_height());
cr->fill();
cr->restore();
}
return false;
}
bool MyDrawing::draw_rectangle(const Cairo::RefPtr<::Cairo::Context> &cr) {
std::cout << "signal square" << std::endl;
cr->save();
cr->set_line_width(10);
cr->set_source_rgba(0., 0., 1., 1.);
cr->rectangle(beginPoint_x, beginPoint_y, width, height);
cr->stroke();
cr->save();
cr->restore();
return false;
}
bool MyDrawing::draw_circle(const Cairo::RefPtr<::Cairo::Context> &cr) {
std::cout << "signal square" << std::endl;
cr->save();
cr->set_line_width(10);
cr->set_source_rgba(0., 0., 1., 1.);
cr->arc(beginPoint_x, beginPoint_y, width, 0, 2 * M_PI);
cr->stroke();
cr->restore();
return false;
}
void MyDrawing::switch_to_circle() {
isDrawRectangle = false;
}
void MyDrawing::switch_to_rectangle() {
isDrawRectangle = true;
}
When I click another shape, the previous shape keeps being displayed on the drawing area and the new shape is drawn on it. On the other hand, when the signal is disconnected, the corresponding shape also disappears from the screen. How could I make sure the shapes keep being displayed?
I am not sure exactly what made you inherit from Gtk::Layout instead of using a standard Gtk::DrawingArea, but I created a simplified (and working) example using a design similar to yours.
The basic idea is that when the user is done drawing a shape (stops the drag and releases the mouse button), the following happens:
The current state of the window (in terms of what is drawn on it) is saved to a Gtk::Pixbuf.
That Gtk::PixBuf is painted on the window.
This means that in 1., the last drawn shaped is also saved in the buffer. When 2. happens, is repainted on the window and hence does not go away. Here is the code, which you will need to adapt a bit to your case. First, a draw helper:
class DrawHelper : public Gtk::Layout
{
public:
DrawHelper();
~DrawHelper();
private:
bool draw_image(const Cairo::RefPtr<::Cairo::Context>& p_context);
bool draw_rectangle(const Cairo::RefPtr<::Cairo::Context>& p_context);
bool add_current_shape(const Cairo::RefPtr<::Cairo::Context>& p_context);
Glib::RefPtr<Gdk::Pixbuf> m_buffer;
double m_startX;
double m_startY;
double m_endX;
double m_endY;
double m_width;
double m_height;
sigc::connection m_drawConnection;
};
which is responsible to do the actual drawing and handle connections. It is implemented like so:
DrawHelper::DrawHelper()
{
// Create a pixel buffer containing the background image:
m_buffer = Gdk::Pixbuf::create_from_file("file.svg", DEFAULT_WIDTH, DEFAULT_HEIGHT);
signal_draw().connect(sigc::mem_fun(*this, &DrawHelper::draw_image));
// Enable signals:
add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
// Save initial pointer position when clicked:
signal_button_press_event().connect(
[this](GdkEventButton* p_event)
{
m_startX = p_event->x;
m_startY = p_event->y;
return true;
});
// Update rectangle when mouse is dragged:
signal_motion_notify_event().connect(
[this](GdkEventMotion* p_event)
{
m_endX = p_event->x;
m_endY = p_event->y;
m_width = m_endX - m_startX;
m_height = m_endY - m_startY;
signal_draw().connect(sigc::mem_fun(*this, &DrawHelper::draw_rectangle));
queue_draw();
return true;
});
// Change background so it includes the shape just drawn by
// the user:
signal_button_release_event().connect(
[this](GdkEventButton* p_event)
{
// Notice we save to connection to later disconnect it:
m_drawConnection = signal_draw().connect(sigc::mem_fun(*this, &DrawHelper::add_current_shape));
return true;
});
}
DrawHelper::~DrawHelper() = default;
bool DrawHelper::draw_image(const Cairo::RefPtr<::Cairo::Context>& p_context)
{
Gdk::Cairo::set_source_pixbuf(p_context, m_buffer, 0, 0);
p_context->paint();
return false;
}
bool DrawHelper::draw_rectangle(const Cairo::RefPtr<::Cairo::Context>& p_context)
{
p_context->save();
p_context->set_line_width(2);
p_context->rectangle(m_startX, m_startY, m_width, m_height);
p_context->stroke();
p_context->restore();
return false;
}
bool DrawHelper::add_current_shape(const Cairo::RefPtr<::Cairo::Context>& p_context)
{
// Save the current drawing, including the last drawn
// shape. This will become the new background (which will
// visually preserve the last drawn shape).
m_buffer = Gdk::Pixbuf::create(p_context->get_target(), 0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT);
Gdk::Cairo::set_source_pixbuf(p_context, m_buffer, 0, 0);
p_context->paint();
// We disconnect the signal because we do not want it
// to keep getting called:
m_drawConnection.disconnect();
return false;
}
Then, a window to hold this helper and display it to the user:
class MyWindow : public Gtk::Window
{
public:
MyWindow();
private:
DrawHelper m_drawHelper;
};
MyWindow::MyWindow()
{
set_default_size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Add draw helper:
add(m_drawHelper);
// Show all widgets:
show_all();
}
Then, the main to run it:
#include <gtkmm.h>
#include <cairo/cairo.h>
#include <cairomm/context.h>
#include <cairomm/surface.h>
constexpr int DEFAULT_WIDTH = 500;
constexpr int DEFAULT_HEIGHT = 500;
// DrawHelper here ...
// MyWindow here ...
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
MyWindow window;
return app->run(window);
}
That being said, I would recommend you use a classic Gtk::DrawingArea instead and overload the on_draw signal handler. This would make all of this easier to understand, and the online documentation would be of more help to you.
If you are still interested, I have another solution for you. Instead of saving the already drawn shape on the background image, you could save their parameters directly and redraw them. I have written an example program that does just this:
#include <memory>
#include <vector>
#include <gtkmm.h>
#include <cairo/cairo.h>
#include <cairomm/context.h>
#include <cairomm/surface.h>
constexpr int DEFAULT_WIDTH = 500;
constexpr int DEFAULT_HEIGHT = 500;
constexpr double LINE_WIDTH = 2.0;
// Free functions for drawing shapes:
namespace
{
void DrawRectangle(const Cairo::RefPtr<Cairo::Context>& p_context,
double p_startX,
double p_startY,
double p_width,
double p_height)
{
p_context->save();
p_context->set_line_width(LINE_WIDTH);
p_context->set_source_rgba(0, 0, 1, 1);
p_context->rectangle(p_startX, p_startY, p_width, p_height);
p_context->stroke();
p_context->restore();
}
void DrawCircle(const Cairo::RefPtr<Cairo::Context>& p_context,
double p_startX,
double p_startY,
double p_width)
{
p_context->save();
p_context->set_line_width(LINE_WIDTH);
p_context->set_source_rgba(0, 0, 1, 1);
p_context->arc(p_startX, p_startY, p_width, 0, 2 * M_PI);
p_context->stroke();
p_context->restore();
}
}
// Shape interface:
//
// A shape represents a 2D geometric shape a user can draw on the
// Drawing area. All shapes implement a 'Draw' method which is where
// the drawing logic resides.
class IShape
{
public:
virtual ~IShape() = default;
virtual void Draw(const Cairo::RefPtr<Cairo::Context>& p_context) = 0;
};
// Rectangle shape:
class Rectangle : public IShape
{
public:
Rectangle(double p_left, double p_up, double p_width, double p_height)
: m_left{p_left}
, m_up{p_up}
, m_width{p_width}
, m_height{p_height}
{}
void Draw(const Cairo::RefPtr<Cairo::Context>& p_context) override
{
DrawRectangle(p_context, m_left, m_up, m_width, m_height);
}
private:
double m_up;
double m_left;
double m_width;
double m_height;
};
// Circle shape:
class Circle : public IShape
{
public:
Circle(double p_cX, double p_cY, double p_radius)
: m_cX{p_cX}
, m_cY{p_cY}
, m_radius{p_radius}
{}
void Draw(const Cairo::RefPtr<Cairo::Context>& p_context) override
{
DrawCircle(p_context, m_cX, m_cY, m_radius);
}
private:
double m_cX;
double m_cY;
double m_radius;
};
// Draw helper:
//
// This class represents the widget onto which the user can drawn. Under
// the hood, this is a Gtk::Drawing area with some signal handlers defined
// to draw shapes on user action.
//
// All drawing occurs in the 'on_draw' method, and all signal handlers to
// is to handle the data (e.g positions, dimentsions, etc) for the 'on_draw'
// method to work appropriately.
//
// The 'SetCurrentShape' method can be used to tell the helper which shape
// to draw.
class DrawHelper : public Gtk::DrawingArea
{
public:
enum class Shape
{
None,
Rectangle,
Circle,
};
DrawHelper()
{
add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
// Click, drag and release signal handlers:
signal_button_press_event().connect( [this](GdkEventButton* p_event){return OnButtonPressed(p_event);} );
signal_motion_notify_event().connect( [this](GdkEventMotion* p_event){return OnMouseMotion(p_event);} );
signal_button_release_event().connect([this](GdkEventButton* p_event){return OnButtonReleased(p_event);});
}
void SetCurrentShape(Shape p_shape)
{
m_currentShape = p_shape;
}
private:
// All drawing occurs here and only here:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& p_context) override
{
// Draw background:
if(!m_buffer)
{
m_buffer = Gdk::Pixbuf::create_from_file("file.svg", DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
Gdk::Cairo::set_source_pixbuf(p_context, m_buffer, 0, 0);
p_context->paint();
// Draw previously drawn shapes:
for(const auto& shape : m_alreadyDrawn)
{
shape->Draw(p_context);
}
// Draw current shape:
if(m_currentShape == Shape::Rectangle)
{
DrawRectangle(p_context, m_startX, m_startY, m_width, m_height);
}
if(m_currentShape == Shape::Circle)
{
DrawCircle(p_context, m_startX, m_startY, m_width);
}
return false;
}
bool OnButtonPressed(GdkEventButton* p_event)
{
m_startX = p_event->x;
m_startY = p_event->y;
return true;
}
bool OnMouseMotion(GdkEventMotion* p_event)
{
m_endX = p_event->x;
m_endY = p_event->y;
m_width = m_endX - m_startX;
m_height = m_endY - m_startY;
queue_draw();
return true;
}
bool OnButtonReleased(GdkEventButton* p_event)
{
if(m_currentShape == Shape::Rectangle)
{
m_alreadyDrawn.push_back(std::make_unique<Rectangle>(m_startX, m_startY, m_width, m_height));
}
if(m_currentShape == Shape::Circle)
{
m_alreadyDrawn.push_back(std::make_unique<Circle>(m_startX, m_startY, m_width));
}
return true;
}
Shape m_currentShape = Shape::None;
Glib::RefPtr<Gdk::Pixbuf> m_buffer;
double m_startX;
double m_startY;
double m_endX;
double m_endY;
double m_width;
double m_height;
std::vector<std::unique_ptr<IShape>> m_alreadyDrawn;
};
// Main window:
//
// This window holds all widgets. Through it, the user can pick a shape
// to draw and use the mouse to draw it.
class MyWindow : public Gtk::Window
{
public:
MyWindow()
: m_drawRectangleBtn{"Rectangle"}
, m_drawCircleBtn{"Circle"}
{
set_default_size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
m_headerBar.set_show_close_button(true);
m_headerBar.pack_start(m_drawRectangleBtn);
m_headerBar.pack_start(m_drawCircleBtn);;
set_titlebar(m_headerBar);
add(m_drawArea);
m_drawRectangleBtn.signal_clicked().connect([this](){OnRectangleBtnClicked();});
m_drawCircleBtn.signal_clicked().connect([this](){OnCircleBtnClicked();});
show_all();
}
private:
Gtk::HeaderBar m_headerBar;
Gtk::Button m_drawRectangleBtn;
Gtk::Button m_drawCircleBtn;
DrawHelper m_drawArea;
void OnRectangleBtnClicked()
{
m_drawArea.SetCurrentShape(DrawHelper::Shape::Rectangle);
}
void OnCircleBtnClicked()
{
m_drawArea.SetCurrentShape(DrawHelper::Shape::Circle);
}
};
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
MyWindow window;
return app->run(window);
}
Each time the user releases the mouse button, the drawn shape is saved (with the parameters at the time of the release) into an std::vector as an IShape, which has a Draw, method. This method can later be called to redraw the shape. Then, in the on_draw handler, all previously drawn shapes are redrawn, leaving them on the screen. Note that I have used a Gtk::DrawingArea here, which is more typical than your approach. I wanted to show you that alternative which, in my opinion, makes cleaner code (no messing around with the handler callbacks).
On a final note, possible enhancements are possible with this (there are more, theses are just some I was thinking about while writing this):
You could reduce performance costs by caching some stuff instead of redrawing everything every time.
You could reduce performance costs by using parameters in the calls to queue_draw so that the whole widget is not constantly redrawn (only the part that changed).
You could use a factory to create the shapes. This would decouple the shape creation from the rest of the code, which would only know the IShape interface. It would also make you program easier to maintain if you ever want to add shapes.

How to implement OnClick, OnMouseDown, OnMouseUp Events of a new component created from TPaintBox?

I am trying to create a new component from the TPaintBox. New component size is Rect(0,0,160,248). I drew two rectangles on newly component and I would like to implement events to each of above rectangle.
Get rectangle is located at Rect(102,43,157,63) and I would like to implement events like OnGetClick , OnGetMouseDown , OnGetMouseUp for this rectangle area .
Set rectangle is located at Rect(102,69,157,89) and I would like to implement events like OnSetClick, OnSetMouseDown, OnSetMouseUp for this rectangle area.
Rest of the new component area is going display values related to the VGauge and I am not included in below code.
class PACKAGE TVGauge : public TPaintBox
{
public:
virtual void __fastcall Paint(void) ;
};
void __fastcall TVGauge::Paint(void)
{
int ind1, ind2 ;
TRect tempR ;
String str;
Canvas->Font->Size = 8 ;
//whole rect
Canvas->Pen->Color = clSilver ;
Canvas->Brush->Color = clBtnFace ;
Canvas->Rectangle(ClientRect) ; //Rect(0,0,160,248)
//----------
Canvas->Pen->Color = clMedGray ;
Canvas->Font->Color = clWindowText ;
Canvas->Brush->Color = clWhite ;
//Get Button ; draw a rectangle and it should act like a button
Canvas->Rectangle(102 , 43 , 157 , 63 ) ;
Canvas->TextOut(112, 48 ,L"Get");
//Set Button ; draw a rectangle and it should act like a button
Canvas->Rectangle(102 , 69 , 157 , 89 ) ;
Canvas->TextOut(112, 74 ,L"Set");
//display values related to the VGauge
//..
//..
if(OnPaint != NULL) OnPaint(this) ;
}
I don't have any idea, how to implement above events for a new component. So, I am hoping to get suggestions to implement this component.
Override the virtual MouseDown() and MouseUp() methods, eg:
enum TVGaugeButton { tvgGetBtn, tvgSetBtn };
typedef void __fastcall (__closure *TVGaugeMouseEvent)(TObject *Sender, TVGaugeButton GaugeButton, TMouseButton MouseButton, TShiftState Shift, int X, int Y);
typedef void __fastcall (__closure *TVGaugeClickEvent)(TObject *Sender, TVGaugeButton GaugeButton);
class PACKAGE TVGauge : public TPaintBox
{
typedef TPaintBox inherited;
private:
bool FMouseDown[2];
TVGaugeMouseEvent FOnGaugeButtonMouseDown;
TVGaugeMouseEvent FOnGaugeButtonMouseUp;
TVGaugeClickEvent FOnGaugeButtonClick;
TRect __fastcall GetButtonRect(TVGaugeButton WhichButton);
protected:
DYNAMIC void __fastcall MouseDown(TMouseButton Button, TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseUp(TMouseButton Button, TShiftState Shift, int X, int Y);
virtual void __fastcall Paint();
public:
__fastcall TVGauge(TComponent *Owner);
virtual void __fastcall SetBounds(int ALeft, int ATop, int AWidth, int AHeight);
__published:
__property TVGaugeClickEvent OnGaugeButtonClick = {read=FOnGaugeButtonClick, write=FOnGaugeButtonClick};
__property TVGaugeMouseEvent OnGaugeButtonMouseDown ={read=FOnGaugeButtonMouseDown, write=FOnGaugeButtonMouseDown};
__property TVGaugeMouseEvent OnGaugeButtonMouseUp = {read=FOnGaugeButtonMouseUp, write=FOnGaugeButtonMouseUp};
};
__fastcall TVGauge::TVGauge(TComponent *Owner)
: TPaintBox(Owner)
{
}
void __fastcall TVGauge::SetBounds(int ALeft, int ATop, int AWidth, int AHeight)
{
inherited::SetBounds(ALeft, ATop, 160, 248);
}
TRect __fastcall TVGauge::GetButtonRect(TVGaugeButton WhichButton)
{
switch (WhichButton)
{
case tvgGetBtn:
return Rect(102, 43, 157, 63);
case tvgSetBtn:
return Rect(102, 69, 157, 89);
}
return Rect(0, 0, 0, 0);
}
void __fastcall TVGauge::MouseDown(TMouseButton Button, TShiftState Shift, int X, int Y)
{
inherited::MouseDown(Button, Shift, X, Y);
for (int i = tvgGetBtn; i <= tvgSetBtn; ++i)
{
TVGaugeButton myBtn = (TVGaugeButton) i;
TRect r = GetButtonRect(myBtn);
if (PtInRect(&r, Point(X, Y)))
{
if (Button == mbLeft)
FMouseDown[i] = true;
if (FOnGaugeButtonMouseDown)
FOnGaugeButtonMouseDown(this, myBtn, Button, Shift, X-r.Left, Y-r.Top);
break;
}
}
}
void __fastcall TVGauge::MouseUp(TMouseButton Button, TShiftState Shift, int X, int Y)
{
inherited::MouseDown(Button, Shift, X, Y);
for (int i = tvgGetBtn; i <= tvgSetBtn; ++i)
{
TVGaugeButton myBtn = (TVGaugeButton) i;
TRect r = GetButtonRect(myBtn);
if (PtInRect(&r, Point(X, Y)))
{
bool bClicked = false;
if (Button == mbLeft)
{
FMouseDown[i] = false;
bClicked = true;
}
if (FOnGaugeButtonMouseUp)
FOnGaugeButtonMouseUp(this, myBtn, Button, Shift, X-r.Left, Y-r.Top);
if ((bClicked) && (FOnGaugeButtonClick))
FOnGaugeButtonClick(this, myBtn);
break;
}
}
}
void __fastcall TVGauge::Paint()
{
TRect tempR;
Canvas->Font->Size = 8;
//whole rect
Canvas->Pen->Color = clSilver;
Canvas->Brush->Color = clBtnFace;
Canvas->Rectangle(ClientRect);
//----------
Canvas->Pen->Color = clMedGray;
Canvas->Font->Color = clWindowText;
Canvas->Brush->Color = clWhite;
//Get Button ; draw a rectangle and it should act like a button
tempR = GetButtonRect(tvgGetBtn);
Canvas->Rectangle(tempR);
Canvas->TextRect(tempR, tempR.Left + 10, tempR.Top + 5, _D("Get"));
//Set Button ; draw a rectangle and it should act like a button
tempR = GetButtonRect(tvgSetBtn);
Canvas->Rectangle(tempR);
Canvas->TextRect(tempR, tempR.Left + 10, tempR.Top + 5, _D("Set"));
//display values related to the VGauge
//..
//..
if (OnPaint)
OnPaint(this);
}
On a side note, you really should derive your component from TGraphicControl directly instead of from TPaintBox. The only difference between TPaintBox and TGraphicControl is that TPaintBox exposes an OnPaint event, where it overrides Paint() to trigger OnPaint. You don't really need the OnPaint event in your component at all, since you are doing all of your own painting (unless you really want users drawing over top of our painting).

cpp private data member SDL_Rect inactive

I have a 7 x 7 matrix of probes that deliver a signal representing the level measured at that point in the area under investigation. I can draw a matric of 7 x 7 rectangles in SDL but I was unable to update the color.
I have a class
class Probe
{
public:
Probe();
~Probe();
void SetColor(int x);
void Set_id(int x, int y, int z);
void Level_id(void);
private:
int OldColor;
int Xcord;
int Ycord;
SDL_Rect rect; //This does not keep the rect that I create?
};
//outside all curly brackets {} I have
Probe Level[7][7];
//I initialize each instance of my class
Level[x][y].Set_id(x, y, z); and I use x and y to create and position the rectangle
// Defininlg rectangles
SDL_Rect rect = {BLOCK_W * Xcord, BLOCK_H * Ycord, BLOCK_W, BLOCK_H};
/*I get what I expected, by the way z sets the color and it is incremented so each rectangle has a different color. */
//I used function SetColor, it failed untill I regenerated rect
//in the SetColor function. I have
private:
SDL_Rect rect;
//It is private why do I need to re-create the SDL_Rect rect?
//Why is it not stored like
private:
int Xcord; // !!? Regards Ian.
If I understand correctly, your class looks something like this:
class Probe {
public:
Probe();
~Probe();
void SetColor(int x);
void Set_id(int x, int y, int z) {
SDL_Rect rect = {BLOCK_W * Xcord, BLOCK_H * Ycord, BLOCK_W, BLOCK_H};
}
private:
int OldColor;
int Xcord;
int Ycord;
SDL_Rect rect;
};
and you're wondering why the "rect" variable won't save as the private one, and how you change the color when drawing an SDL_Rect?
First of all, your "rect" isn't getting saved because you are defining a new "rect" variable in your function. What you should do is either of these:
rect = {BLOCK_W * Xcord, BLOCK_H * Ycord, BLOCK_W, BLOCK_H};
or
rect.x = BLOCK_W * Xcord;
rect.y = BLOCK_H * Ycord;
rect.w = BLOCK_W;
rect.h = BLOCK_H;
And to change the color of the rendering (assuming you are using the SDL_RenderDrawRect function, as that is the only one I know of that you can draw SDL_Rects with), you simply call:
SDL_SetRenderDrawColor(int r, int g, int b, int a);
right before you call the SDL_RenderDrawRect function (every time).
If you do not do this, and call the SetRenderDrawColor function somewhere else, your color will change to that color.

Qt: How to create a clearly visible glow effect for a QLabel? (e.g. using QGraphicsDropShadowEffect)

I am trying to add a glow effect to a QLabel so that it looks like the time display in the following picture:
I found out that you can "misuse" a QGraphicsDropShadowEffect for this:
QGraphicsDropShadowEffect * dse = new QGraphicsDropShadowEffect();
dse->setBlurRadius(10);
dse->setOffset(0);
dse->setColor(QColor(255, 255, 255));
ui.label->setGraphicsEffect(dse);
However, the resulting effect is too weak, you can barely see it:
Unfortunately, you can not modify the strength of the effect, only color and blur radius.
One idea would be to apply multiple QGraphicsDropShadowEffect to the label, so that it gets more visible due to overlapping. But calling ui.label->setGraphicsEffect(dse); will always delete any previous effects, i.e. I was not able to set multiple QGraphicsEffect to the same object.
Any ideas how you can create a clearly visible glow effect with Qt?
Meanwhile, I tinkered my own graphics effect based on QGraphicsBlurEffect and using parts of this answer. If you know any better solutions, let me know.
qgraphicsgloweffect.h:
#pragma once
#include <QGraphicsEffect>
#include <QGraphicsBlurEffect>
#include <QGraphicsColorizeEffect>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QPainter>
class QGraphicsGlowEffect :
public QGraphicsEffect
{
public:
explicit QGraphicsGlowEffect(QObject *parent = 0);
QRectF boundingRectFor(const QRectF &rect) const;
void setColor(QColor value);
void setStrength(int value);
void setBlurRadius(qreal value);
QColor color() const;
int strength() const;
qreal blurRadius() const;
protected:
void draw(QPainter* painter);
private:
static QPixmap applyEffectToPixmap(QPixmap src, QGraphicsEffect *effect, int extent);
int _extent = 5;
QColor _color = QColor(255, 255, 255);
int _strength = 3;
qreal _blurRadius = 5.0;
};
qgraphicsgloweffect.cpp:
#include "QGraphicsGlowEffect.h"
#include <QtCore\qmath.h>
QGraphicsGlowEffect::QGraphicsGlowEffect(QObject *parent) : QGraphicsEffect(parent)
{
}
void QGraphicsGlowEffect::setColor(QColor value) {
_color = value;
}
void QGraphicsGlowEffect::setStrength(int value) {
_strength = value;
}
void QGraphicsGlowEffect::setBlurRadius(qreal value) {
_blurRadius = value;
_extent = qCeil(value);
updateBoundingRect();
}
QColor QGraphicsGlowEffect::color() const {
return _color;
}
int QGraphicsGlowEffect::strength() const {
return _strength;
}
qreal QGraphicsGlowEffect::blurRadius() const {
return _blurRadius;
}
QRectF QGraphicsGlowEffect::boundingRectFor(const QRectF &rect) const {
return QRect(
rect.left() - _extent,
rect.top() - _extent,
rect.width() + 2 * _extent,
rect.height() + 2 * _extent);
}
void QGraphicsGlowEffect::draw(QPainter* painter) {
QPoint offset;
QPixmap source = sourcePixmap(Qt::LogicalCoordinates, &offset);
QPixmap glow;
QGraphicsColorizeEffect *colorize = new QGraphicsColorizeEffect;
colorize->setColor(_color);
colorize->setStrength(1);
glow = applyEffectToPixmap(source, colorize, 0);
QGraphicsBlurEffect *blur = new QGraphicsBlurEffect;
blur->setBlurRadius(_blurRadius);
glow = applyEffectToPixmap(glow, blur, _extent);
for (int i = 0; i < _strength; i++)
painter->drawPixmap(offset - QPoint(_extent, _extent), glow);
drawSource(painter);
}
QPixmap QGraphicsGlowEffect::applyEffectToPixmap(
QPixmap src, QGraphicsEffect *effect, int extent)
{
if (src.isNull()) return QPixmap();
if (!effect) return src;
QGraphicsScene scene;
QGraphicsPixmapItem item;
item.setPixmap(src);
item.setGraphicsEffect(effect);
scene.addItem(&item);
QSize size = src.size() + QSize(extent * 2, extent * 2);
QPixmap res(size.width(), size.height());
res.fill(Qt::transparent);
QPainter ptr(&res);
scene.render(&ptr, QRectF(), QRectF(-extent, -extent, size.width(), size.height()));
return res;
}
Then you can use it like:
QGraphicsGlowEffect * glow = new QGraphicsGlowEffect();
glow->setStrength(4);
glow->setBlurRadius(7);
ui.label->setGraphicsEffect(glow);
This results in a nice glow effect: