Replacing QPen's - How Do I Delete - c++

I am developing a GUI which will allow the user to modify ellipses using mouse/QSpinBox events on top of a background picture.
I want to set it up so that when a user clicks on an ellipse, the ellipse changes color and has been "selected."
I am using QGraphicsView/Scene with QGraphicsEllipseItem. Here in lies my problem, the setPen(QPen & const) call is a reference thus:
If I allocate a pen on the stack and pass it in, I get a segfault.
If I allocate a pen on the heap, it doesn't get deleted when I set a new pen
The accessor method returns a copy of the pen
Anyways, I must be approaching this wrong, could any offer any suggestions?
P.S. - I would like to avoid making yet ANOTHER member variable. I am going to have many ellipses, each one should not have its own pen variable!
This code will cause a segfault:
void MyClass::SetupEllipses()
{
QPen pen();
pen.setColor(QColor(255,0,0));
pen.setWidth(2);
m_ellipse = new QGraphicsItemEllipse(); //This is a member variable of MyClass
m_ellipse->setRect(some ssize here);
m_ellipse->setPen(pen);
m_graphicsview->scene()->addItem(m_ellipse); //m_graphicsview is also a member variable of MyClass and has had a scene added to it.
}

If I allocate a pen on the stack and pass it in, I get a segfault.
That's the right way - if you get a segfault, that's certainly not due to the QPen.
Post your code and debugger stack trace.
Each QGraphicsEllipseItem will store a (light-weight) copy of the QPen anyway, so creating QPen instances on the heap is nonsensical and error-prone.
Do not worry about too many QPen instances. QPen uses implicit sharing, i.e. if you share the same unmodified pen, the copies are cheap.

Related

How do I access the associated Renderer object from within a QQuickFramebufferObject

I tried to store it as a member pointer on creation, so it can be accessed later:
QQuickFramebufferObject::Renderer* MyItem::createRenderer() const {
m_renderer = new MyItemRenderer(this);
return m_renderer;
}
... but this doesn't work - Qt requires createRenderer to be a const method, so I can't assign to m_renderer within it. I could use mutable but that's a hack and is risky because it may break assumptions in Qt internals.
Any proper way?
I thought of a way:
In MyItemRenderer::synchronize set the item's renderer to this. I don't like this very much, because it's abuse of synchronize, but it's certainly much better than mutable.
What about this?
QQuickFramebufferObject::Renderer* MyItem::createRenderer() const {
return new MyItemRenderer();
}
More information here. At the end of this page, it is stated the following:
However, there is a special case when the FBO has to get recreated regardless: when moving the window to a screen with a different device pixel ratio. For example moving a window between a retina and non-retina screen on OS X systems will inherently need a new, double or half sized, framebuffer, even when the window dimensions are the same in device independent units. Just like with ordinary resizes, Qt is prepared to handle this by requesting a new framebuffer object with a different size when necessary. One possible pitfall here is the applications’ caching of the results of the factory functions: avoid this. createFramebufferObject() and createRenderer() must never cache their return value. Just create a new instance and return it. Keep it simple.

Using <curses.h> in multiple classes

I have three different classes. Each class is responsible for drawing a specific thing using ncurses.
I must draw all the three things at once. One of the classes is responsible for a board, and the other two classes draws something inside the board.
I got it to work, but the problem is that whenever I use clear, it clears the board and the other two things; I want the board to stay and never get erased. I want to clear the drawing that only the specific class is responsible for.
For example, let's say I have a board, and I have a person class and a dog class. When I call the draw method in the person class, it draws me the person inside the board, but whenever I move it to a different point, it draws a new person, but it never clears the old person.
Same thing with the dog unless I use the clear method from the curses.h but it erases and clears everything including the board and the dog.
However, I only want to erase the person and not everything. Is there any built-in method to use from ncurses other than clear or erase, or anything that clears the whole screen?
As Ivan Rubinson suggested in the comments, the usual approach is to clear everything then redraw everything every frame/repaint.
According to the documentation, clear() should clear the whole screen:
The clear(), erase(), wclear() and werase() functions clear every position in the current or specified window.
The clear() and wclear() functions also achieve the same effect as calling clearok(), so that the window is cleared completely on the next call to wrefresh() for the window and is redrawn in its entirety.
Your main loop / drawing code should look something like this (pseudocode):
clear(); // clear everything
// redraw everything
for(auto& widget : drawables)
{
widget.draw();
}
// display
refresh();
You can do this by creating a window for each of the objects, and removing them when they're done. That way, you need only clear the window that you're removing, refresh it and then delete it.
That is, use newwin, waddstr, wrefresh, wclear and delwin rather than addstr, refresh and clear.

QGraphicsItems can only be created on the heap?

In the constructor of a QMainWindow...
A new circle is created on the heap and passed to the scene, the circle shows up, everythin is working:
QGraphicsScene * scene = new QGraphicsScene(this);
CustomGraphicsView * view = new CustomGraphicsView(scene,this);
QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem (100,100,30,30);
ellipse->setPen(QPen(Qt::green,10));
scene->addItem(ellipse);
Now we create circle on the stack and pass it by reference. But this time the cricle will never shows up:
QGraphicsScene * scene = new QGraphicsScene(this);
CustomGraphicsView * view = new CustomGraphicsView(scene,this);
QGraphicsEllipseItem ellipse(100,100,30,30);
ellipse.setPen(QPen(Qt::green,10));
scene->addItem(&ellipse);
I guess it gets destroyed before it shows up. But I don't understand why? Why is this not working and what is the rule behind this behaviour?
From http://doc.qt.io/qt-4.8/qgraphicsscene.html#addItem
Adds or moves the item and all its childen to this scene. This scene takes ownership of the item.
A QGraphicsScene cannot take ownership of an item if it is created on the stack. This implies that the item has to be created on the heap.
QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem (100,100,30,30);
ellipse->setPen(QPen(Qt::green,10));
scene->addItem(ellipse);
This works because the scene object is in charge of deleteing the object which you've allocated via new. Somewhere in the depths of the Qt framework, there will be a call along the lines of:
delete item;
Now with this code:
QGraphicsEllipseItem ellipse(100,100,30,30);
ellipse.setPen(QPen(Qt::green,10));
scene->addItem(&ellipse);
Consider what happens. The delete item; is still there, but it will be applied not only on a pointer to a local object but (probably, depending on the program flow) also to a pointer to an object which no longer lives. The code which performs the delete simply has no idea that the object was not created via new.
Any attempt to destroy an object more than once is undefined behaviour, as is trying to use an object which was already destroyed. Your question is a good example of why passing around pointers to local objects should be treated as an especially dangerous action which requires extra caution.
By the way:
But this time the cricle will never shows up:
That's a mere coincidence. Undefined behaviour means that everything can happen.

Change type of mainclass object during runtime to subclass

First of all I'm sorry for the title, couldnt come up with something short to describe my problem.
What I have is a "text-based adventure game". I have a main class that's called Game, where I have the game loop etc, I also have a class called Screen, and a childclass to Screen called Screen_One, and Screen_Two and so on, for as many different "Screens" I want in my game.
So what I have inside Screen, is an sf::Texture and sf::Sprite. Screen_One and Two both initialize those in their respective constructors with different values (in this case its just a background texture).
And here comes my problem, my Game class has an object of type Screen, which I want to change depending on where in the game I am, so basically I want to initialize Screen as a Screen_One object in the Game constructor, and when I progress in the game to Screen_Two I want to change that Screen object to a Screen_Two object and therefore get the right background for the screen I'm currently on.
What I have now (which doesn't work is something like this)
Screen_One's constructor
Screen_One::Screen_One(void)
{
Texture.loadFromFile("filename.png"); // background for screen_one
Sprite.setTexture(Texture);
}
inside the Game class:
Game::Game(void) : mWindow(sf::VideoMode(640, 480), "Game") // Game constructor
{
sc = Screen_One(); // sc is a declared like "Screen sc" in Game.h
}
void Game::render() // this function runs inside the game loop
{
//draw some stuff
mWindow.draw(this->sc.Sprite);
}
this code runs, but it just gives me a white background (so basically it draws nothing). So this way of doing it obviously doesnt work but I hope you can understand what Im trying to do and maybe fix or give me another way of achieving the same thing.
EDIT: tl;dr can I have an object of type Screen, and then during runtime assign the Screen object to Screen_One or Screen_Two in order to for example change background.
In this code
sc = Screen_One();
what is happening is that the Screen part of Screen_One is being copied into sc, not the whole object, as sc doesn't have enough space for it. This is assuming you haven't overridden the = operator.
To fix your problem, sc should be a pointer to Screen. The declaration would be
Screen *sc;
Then in the Game constructor, initialize the pointer to a new object of type Screen_One:
sc = new Screen_One();
When you want to change screens, delete the old screen and make sc point to a new instance of Screen_Two:
delete sc;
sc = new Screen_Two();

Tiling with QGraphicsScene and QGraphicsView

I'm creating a image visualizer that open large images(2gb+) in Qt.
I'm doing this by breaking the large image into several tiles of 512X512. I then load a QGraphicsScene of the original image size and use addPixmap to add each tile onto the QGraphic Scene. So ultimately it looks like a huge image to the end user when in fact it is a continuous array of smaller images stuck together on the scene.First of is this a good approach?
Trying to load all the tiles onto the scene takes up a lot of memory. So I'm thinking of only loading the tiles that are visible in the view. I've already managed to subclass QGraphicsScene and override its drag event thus enabling me to know which tiles need to be loaded next based on movement. My problem is tracking movement on the scrollbars. Is there any way I can create an event that get called every time the scrollbar moves. Subclassing QGraphicsView in not an option.
QGraphicsScene is smart enough not to render what isn't visible, so here's what you need to do:
Instead of loading and adding pixmaps, add classes that wrap the pixmap, and only load it when they are first rendered. (Computer scientists like to call this a "proxy pattern"). You could then unload the pixmap based on a timer. (They would be transparently re-loaded if unloaded too soon.) You could even notify this proxy path of the current zoom level, so that it loads lower resolution images when they will be rendered smaller.
Edit: here's some code to get you started. Note that everything that QGraphicsScene draws is a QGraphicsItem, (if you call ::addPixmap, it's converted to a ...GraphicsItem behind the scenes), so that's what you want to subclass:
(I haven't even compiled this, so "caveat lector", but it's doing the right thing ;)
class MyPixmap: public QGraphicsItem{
public:
// make sure to set `item` to nullptr in the constructor
MyPixmap()
: QGraphicsItem(...), item(nullptr){
}
// you will need to add a destructor
// (and probably a copy constructor and assignment operator)
QRectF boundingRect() const{
// return the size
return QRectF( ... );
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget){
if(nullptr == item){
// load item:
item = new QGraphicsPixmapItem( ... );
}
item->paint(painter, option, widget);
}
private:
// you'll probably want to store information about where you're
// going to load the pixmap from, too
QGraphicsPixmapItem *item;
};
then you can add your pixmaps to the QGraphicsScene using QGraphicsScene::addItem(...)
Although an answer has already been chosen, I'd like to express my opinion.
I don't like the selected answer, especially because of that usage of timers. A timer to unload the pixmaps? Say that the user actually wants to take a good look at the image, and after a couple of seconds - bam, the image is unloaded, he will have to do something in order the image to reappear. Or may be you will put another timer, that loads the pixmaps after another couple of seconds? Or you will check among your thousand of items if they are visible? Not only is this very very irritating and wrong, but that means that your program will be using resources all the time. Say the user minimizes you program and plays a movie, he will wonder why on earth my movie is freezing every couple of seconds...
Well, if I misunderstood the proposed idea of using timers, execuse me.
Actually the idea that mmutz suggested is better. It reminded me of the Mandelbrot example. Take a look at it. Instead of calculating what to draw you can rewrite this part to loading that part of the image that you need to show.
In conclusion I will propose another solution using QGraphicsView in a much simpler way:
1) check the size of the image without loading the image (use QImageReader)
2) make your scene's size equal to that of the image
3) instead of using pixmap items reimplement the DrawBackground() function. One of the parameters will give you the new exposed rectangle - meaning that if the user scrolls just a little bit, you will load and draw only this new part(to load only part of an image use setClipRect() and then read() methods of the QImageReader class). If there are some transformations you can get them from the other parameter(which is QPainter) and apply them to the image before you draw it.
In my opinion the best solution will be to combine my solution with the threading shown in the Mandelbrot example.
The only problem that I can think of now is if the user zooms out with a big scale factor. Then you will need a lot of resources for some time to load and scale a huge image. Well I see now that there is some function of the QImageReader that I haven't tried yet - setScaledSize(), which maybe do just what we need - if you set a scale size and then load the image maybe it won't load first the entire image – try it. Another way is just to limit the scale factor, a thing that you should do anyway if you stick to the method with the pixmap items.
Hope this helps.
Unless you absolutely need the view to be a QGraphicsView (e.g. because you place other objects on top of the large background pixmap), I'd really recommend just subclassing QAbstractScrollArea and reimplementing scrollContentsBy() and paintEvent().
Add in a LRU cache of pixmaps (see QPixmapCache for inspiration, though that one is global), and make the paintEvent() pull used pixmaps to the front, and be set.
If this sounds like more work than the QGraphicsItem, believe me, it's not :)