How to remove a QWidget from a QGraphicsScene? - c++

I am trying to accomplish the following:
(1) add a QPushButton to a QGraphicsScene with addWidget()
(2) display this QGraphicsScene with QGraphicsView
(3) remove that button later (for example, when I click it).
I have done (1) and (2) but I have no idea how to do (3). Another question on this site asked a similar question, one of whose answers suggested something like the following:
QPushButton* startGame = new QPushButton("Start Game");
QGraphicsScene* scene = new QGraphicsScene;
QGraphicsProxyWidget* proxy = scene->addWidget(startGame);
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
scene->removeItem(proxy);// without this everything is fine
But I received this error: no matching function for call to QGraphicsScene::removeItem(QGraphicsProxyWidget*&)'. Why is this? How can I solve it?
Btw, I am using Qt creator 4.7.0 based on Qt 5.11.1 (MSVC2015, 32 bit).
Any help is greatly appreciated.

Related

How to change the position of QTextEdit

I have a QTextEdit object that I use to display text, line by line, for OSD purposes. I would like to be able to change the position of this text box around the window without using a mouse.
The issue is that when I "move" the QTextEdit text box, all of the previous text boxs continue to exist alongside the new one.
I have been using setGeometry(x,y,w,h) to configure the position and size of the text box when I create it. I have also experimented with resize(w,h) and move(x,y), which seem to work similarly to setGeometry(). If all I want to do is to show some text, is QTextEdit the right part or is there something better? I am using Qt 4.8.
header.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void draw_osd();
QTextEdit *osd; // Make osd a member
public slots:
void val_changed();
};
code.cc
// These globals are set in a separate thread
// that takes user input to resize the QTextEdit box geometry.
int g_xpos;
int g_ypos;
int g_w;
int g_h;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setAttribute(Qt::WA_NoBackground);
setWindowFlags(Qt::FramelessWindowHint);
resize(1920,1080);
// Create a new QTextEdit text box
osd = new QTextEdit(this);
// Set a Qtimer to update the OSD display every 1 second
QTimer *timer = new QTimer();
connect(timer, SIGNAL(timeout()), SLOT(val_changed()));
timer->start(1000);
}
MainWindow::val_changed()
{
osd->setReadOnly(true);
if (g_val_update)
{
g_val_update = false;
// Update the OSD
draw_osd();
}
}
void MainWindow::draw_osd()
{
// Clear the old data from the OSD
osd->clear();
// Set the geometry based on user input
osd->setGeometry(g_xpos, g_ypos, g_w, g_h);
QString arg = QString("OSD position %1 %2).arg(g_xpos).arg(g_ypos);
osd->append(arg);
QString arg = QString("OSD size %1 %2).arg(g_w).arg(g_h);
osd->append(arg);
osd->show();
}
I can append osd with new text from the user. So long as I do not change the geometry, the text will update in the text box nicely. I suspect the issue has to do with osd going out of scope. But if that is on the right track, why can I append the text? Am I on the right track?
Update 6/09/2017:
I made some changes to the code so that QTextEdit *osd is a member of the MainWindow class. This should limit the number of QTextEdit objects I create to one, yet I still get old data when I change the position.
Update 6/12/2017:
I found a bug after making QTextEdit *osd a member of the class where the text would be added to the same text box but never cleared. I did not see this earlier because I was restricting the size of the box to perfectly fit my text. To remedy this, I added osd->clear(); to the top of the draw_osd() function. This change has been updated in the code above.
However, I still have the original issue of stale copies when moving or resizing the geometry. I should only ever have one instance of QTextEdit, as only one is created in the MainWindow constructor and it is never destroyed. Could there be an issue with QTextEdit itself that is preventing me from moving it? As suggested by #Scab, I will try this with QLabel and see if it is an improvement.
Update 6/12/2017 #2:
I modified this code to work with QLabels instead of a QTextEdit. However I get the exact same issue when I go to change the geometry, and now I have two rendered QLabels instead of one.
Update 6/12/2017 #3:
Success! Well, sort of. I have been developing this within an embedded device, so I decided to see what happens when I test it in Qt Creator. Lo and behold, it works! This is good news since it does prove my code is working as intended. I can focus my efforts towards debugging the Linux framebuffer now. Thank you all who offered their help.
TL;DR
How can I change the geometry of a QTextEdit object without having previous versions also rendered?
How can I change the geometry of a QTextEdit object without having previous versions also rendered?
setGeometry is fine , but you should call QTextEdit::clear() to clear the previous data displayed in yourQTextEdit before writing a new text.
If all I want to do is to show some text
Then you might also find interesting to have a look at QLabel.
Qt::WA_NoBackground is obsolete. It will give the desired transparent background effect, but has undefined behavior like retaining copies of widgets.
To keep the transparent background,
Solution 1:
Use the command line arguments
-bg NoBackground
Solution 2:
Use WA_OpaguePaintEvent instead.
http://doc.qt.io/qt-4.8/qt.html

QGraphicsView rendering its sub widget's sub widget weirdly

I'm working on a music/MIDI editor using C++ and Qt.
The develop was smooth until I met an issue that QGraphicsView is rendering weirdly with its child widget's child widget.
(wow, that's confusing)
This is the code I used to add my sub widgets to a QGraphicsView
pianoPlain = new QawPianoPlain();
keyBoard = new QawPianoKeyBoard();
scene->addWidget(pianoPlain);
scene->addWidget(keyBoard);
keyBoard->setGeometry(0,0,100,540);
keyBoard->setKeyNum(36);
pianoPlain->setGeometry(100,0,500,540);
pianoPlain->setStriptNum(36);
The code makes something like this.
pianoRoll -> keyBoard -> key
...............|-> pianoPlaom -> noteStript
Where pianoPlain and keyBoard are classes I inherit from GraphicsView and key and noteStript are both inherit from QWidget.
Here is a picture of what happens:
https://drive.google.com/file/d/0BzYLJIsbhhuVZ0RMdzRqV01Cd2M/view?usp=sharing
And this is picture of a normal keyBoard which is placed directy in MainWindow(left) and a screwed up one placed in QGraphicsView(right)
(I have disable back/while key in this picture)
https://drive.google.com/file/d/0BzYLJIsbhhuVSDFhSXRVM0dHWnM/view?usp=sharing
It seems that whenever pianoPlain and keyBoard are placed in a QGtraphicsView, it will be screwed.
Anyone know what's happening? How can I fix this?
Qt : 5.4.1
OS : KUubutu 15.04 AMD64
Compiler : clang 3.6.0/ GCC 4.9.2
I would suggest pianoPlain and keyBoard should use a class that inherits QGraphicsItem - for example QGraphicsPixmapItem or QGraphicsProxyWidget.
You then would add these to the scene using your scene->addWidget() method.
The scene itself should then be part of a QGraphicsView.
For example:
QGraphicsView *view = new QGraphicsView();
QGraphicsScene* scene = new QGraphicsScene(view);
view->setScene(scene_);
// No need to parent these
pianoPlain = new QawPianoPlain();
keyBoard = new QawPianoKeyBoard();
// This will work if QawPianoPlain and QawPianoKeyBoard inherit QWidget
QGraphicsProxyWidget* pianoProxy = scene_->addWidget(pianoPlain);
QGraphicsProxyWidget* keyBoardProxy = scene_->addWidget(keyBoard );
Use the proxy widget for any transformations/animations.

Adding an image to QtGraphicsView?

Hi iv looked at other questions and solutions for this but none seem to help for my specific problem.
Im simply trying to add a picture to my GraphicsView that i have added using Qt designer
.cpp
void test::populateScene()
{
QImage image(":/images/myFile.png");
QGraphicsPixmapItem item(QPixmap::fromImage(image));
QGraphicsScene *scene = new QGraphicsScene;
scene->addItem(&item);
ui->graphicsView->setScene(scene);
}
i have the necessaary includes but when i click run the program just puts up a not responding message straight away
i have no compiler errors just hit run and then then get test.exe is not responding
any ideas, this seems like it should be really simple but i cant work out why this isnt right (mainly due to no compiler errors to look through and find the cause of the crash)
[SOLVED]
QImage image(":/images/myFile.png");
QGraphicsScene *scene = new QGraphicsScene();
scene->addPixmap(QPixmap::fromImage(image));
scene->setSceneRect(0,0,image.width(),image.height());
ui->graphicsView->setScene(scene);
No other solution i found here or elsewhere for getting an image into a graphicsview have worked so i will post this to help others now my problem is solved feel free to ask questions
You have to allocate your new item on the heap!
http://harmattan-dev.nokia.com/docs/library/html/qt4/qgraphicsscene.html#addItem
Adds or moves the item and all its childen to this scene. This scene takes ownership of the item.
The description of the function is quite clear: it will need pointer that is valid for more that the scope of your function, so that it can take ownership of it.
void test::populateScene()
{
QImage image(":/images/myFile.png");
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
QGraphicsScene *scene = new QGraphicsScene;
scene->addItem(item);
ui->graphicsView->setScene(scene);
}

Qt Gridlayout doesn't realign GUI elements

I have the following code in the ctor of my main window widget, in my Qt App. No matter how I align the buttons added to the QGridLayout, they always stay in the upper left corner, on top of each other.
Can anybody tell me what I've done wrong, I can't find it.
btn_File= new QPushButton("&File", this);
btn_Close = new QPushButton("&Close", this);
btn_File->setAutoFillBackground(true);
btn_Close->setAutoFillBackground(true);
QGridLayout * layout = new QGridLayout(this);
layout->setContentsMargins(20,20,10,10);
layout->setSpacing(5);
layout->addWidget(btn_File,2,2, Qt::AlignRight);
layout->addWidget(btn_Close,1,1);
this->setLayout(layout);
EDIT: It seems that only the btn_Close is being drawn. I just tried to add a QComboBox to the grid, and it doesn't show up.
The problem was that my main window was derived from QMainWindow, in which you need to add a CentralWidget before adding GUI elements. I changed my main window to derive from QWidget instead, and now it works.
tried calling this->adjustSize() at the end?
qt layouts really suck!
alignment on qgridlayout depends on the size of the objects, how many cols an object needs, and the size of the biggest object inserted.. so it is very difficult to put objects as you want...
i suggest to use setGeometry or move instead!

GtkVBox Qt equivalent

In GTK, I used to have a window to which I gtk_container_add()'d a GtkVBox. Then I could pack widgets I wanted to the GtkVBox to have them appear in the window.
Now I've decided to try out Qt, but I can't seem to figure out how to do this in Qt.
Right now what I've done is create a QMainWindow, but I found that you can only pack one main widget into it, which is obviously quite limiting. So I wanted to create something like the GtkVBox and use that as the main widget and then add other widgets to this box.
What I've found by Googling are however only the Q3VBox widget, which seems to be what I want, but is deprecated, and the QVBoxLayout.
I tried to use the QVBoxLayout, but it tells me that I cannot change the layout of my QMainWindow since it already has a layout.
Edit: Here is how I do it (this is in the constructor):
box = new QVBoxLayout;
setLayout(box)
It compiles fine, but during runtime, it prints on the console:
QWidget::setLayout: Attempting to set QLayout "" on HCGWindow "", which already has a layout
(HCGWindow is my app's window, which is a subclass of QMainWindow)
So, how can I create something similar to a GtkVBox in Qt, and if the solution is the Q3VBox, why is it deprecated and what other thing should I use?
Thanks
In fact, here is the recommended solution provided by the Qt documentation.
You should create a QVBoxLayout and add the widgets you want in it. After that, you set the layout on another empty widget and then you set this widget as the central widget of the QMainWindow subclass. Here is an example in code:
QWidget* widget1 = new QWidget(); // This could be anything subclassing QWidget.
QWidget* widget2 = new QWidget();
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(widget1);
layout->addWidget(widget2);
QWidget* central = new QWidget(); // Only a containing QWidget.
central->setLayout(layout);
this->setCentralWidget(central);
Now, you QMainWindow subclass should have the two QWidgets in it, arranged in a QVBoxLayout.
Note here that I did not give any parent to anyone. You could have done it, but when you call addWidget or setCentralWidget, the ownership of the widget (and the layout) is given to the containing class.
If you read a bit about Qt, you'll know that this allows the parent to destroy his children when he is about to be destroyed itself.
Finally, note that QMainWindow is an exception and, from what I know, is the only class with setCentralWidget as a method. If you attempt to create a QWidget subclass, you will be able to use setLayout (as shown in the example above).
Hope this helps.
Try to create QVBoxLayout instance, add your widgets to it and add (not replace) this layout to main window's layout.
Note, QLayout class has no member addLayout, but subclasses has one.
Firstly you must get and remember classname of main window's layout:
qDebug(this.layout()->objectName);
Then add your QVBoxLayout to window's layout:
dynamic_cast<YourWindowLayoutClass>(
this.layout())->addLayout(your_qvboxlayout_object);
I hope it will work.