Given a QGraphicsScene, or QGraphicsView, is it possible to create an image file (preferably PNG or JPG)? If yes, how?
After just dealing with this problem, there's enough improvement here to warrant a new answer:
scene->clearSelection(); // Selections would also render to the file
scene->setSceneRect(scene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
QImage image(scene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
image.fill(Qt::transparent); // Start all pixels transparent
QPainter painter(&image);
scene->render(&painter);
image.save("file_name.png");
I have not tried this, but this is the idea of how to do it.
You can do this in several ways
One form is as follows:
QGraphicsView* view = new QGraphicsView(scene,this);
QString fileName = "file_name.png";
QPixmap pixMap = view->grab(view->sceneRect().toRect());
pixMap.save(fileName);
//Uses QWidget::grab function to create a pixmap and paints the QGraphicsView inside it.
The other is to use the render function QGraphicsScene::render():
QImage image(fn);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
scene.render(&painter);
image.save("file_name.png")
grabWidget is deprecated, use grab. And you can use a QFileDialog
QString fileName= QFileDialog::getSaveFileName(this, "Save image", QCoreApplication::applicationDirPath(), "BMP Files (*.bmp);;JPEG (*.JPEG);;PNG (*.png)" );
if (!fileName.isNull())
{
QPixmap pixMap = this->ui->graphicsView->grab();
pixMap.save(fileName);
}
Related
I have a GUI composed of a QTableView with 18 columns and a QGraphicsView where I upload images using a button. I am able to draw a box on a region of interests (ROI) and as soon as I do that with a mouse right click I open up a small dialog. This dialog is composed of a TabWidget with two pages. The first page has a small QGraphicsView that carries the cropped image (say Image A) captured with the previous box drawn. The second page of the TabWidget has also another QGraphicsView that I use to upload a previously saved .jpg image (say Image B) that is on my Desktop. As soon as I hit ok the information on this dialog will be transferred to the first row of the QTableView. Image A will be stoted in one column (column 17 to be precise) and Image B in an additional column too (column 18 to be precise).
I was able to store the cropped Image A (which was the most difficult part because I had to work on understanding the conversion format between Qt and openCV and vice versa) in the QTableView but not Image B (which I handle as simple image that I saved on my Desktop).
I tried different options: option 1: I created a function with which I handle the image that I upload on the second page of the TabWidget (Image B), option 2: I tried to debug to narrow the problem and it seems that the compiler is not seeing the image I am trying to store.
I am attaching the most important part of the code that are carrying this bug:
In clipscene.h is how I declare the most important variables and functions I use:
class clipScene : public QDialog
{
Q_OBJECT
public:
explicit clipScene(QWidget *parent = 0);
~clipScene();
void setImage(QImage img);
void setClassifiedImage(QImage img);
SelectionData getData();
void setData(SelectionData newdata);
private:
SelectionData returnData;
Ui::clipScene *ui;
QImage simg;
QImage featureClassified;
};
In clipscene.cpp this is the function setClassifiedImage I use to handle Image B
void clipScene::setClassifiedImage(QImage img)
{
featureClassified = img;
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(featureClassified));
workingImageScene->addItem(item);
}
In clipscene.cpp using setImage I successfully handle the Qt and openCV conversion of the cropped image
void clipScene::setImage(QImage img)
{
simg = img;
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(simg));
scene->addItem(item);
ui->graphicsViewClipped->show();
cv::Mat input = cv::Mat(simg.height(), simg.width(), CV_16UC3, simg.bits(), simg.bytesPerLine());
// .....operations...
// .....operations...
}
In clipscene.cpp the following img1 represent the cropped Image A and the img2 represent Image B (that is currently nor stored neither passsed)
void clipScene::on_acceptBtn_clicked()
{
// .....operations...
// This will save the cropped Image A successfully
QPixmap img1;
img1.convertFromImage(simg);
QByteArray img1Array;
QBuffer buffer1(&img1Array);
buffer1.open(QIODevice::WriteOnly);
img1.save(&buffer1, "PNG");
returnData.mSave = img1Array;
// This will save a different Image B
// But here the compiler says that no arguments are being passed
QPixmap img2;
QByteArray img2Array;
QBuffer buffer2(&img2Array);
buffer2.open(QIODevice::WriteOnly);
img2.save(&buffer2, "PNG");
returnData.mClassImg = img2Array;
}
Finally when all information are passed and stored on the QTableView I am able to DoubleClick on one row of the QTableView and the same dialog I used before for manually storing the data will pop up with all information recorded and both images (Image A and Image B).
The part of the code that does this on MainWindow (and where the bug is connected) is below:
void MainWindow::onTableClick(const QModelIndex &index)
{
int row = index.row();
SelectionData currentData;
currentData.mName = index.sibling(row, 1).data().toString();
// ....additional data....
// ....additional data...
currentData.mSave = index.sibling(row, 17).data().toByteArray();
currentData.mClassImg = index.sibling(row, 18).data().toByteArray();
QPixmap iconPix;
if(!iconPix.loadFromData(index.sibling(row, 17).data().toByteArray())) {
}
QPixmap iconPix2;
if(!iconPix2.loadFromData(index.sibling(row, 18).data().toByteArray())) {
}
clipScene d(this);
d.setData(currentData);
d.setImage(iconPix.toImage());
d.setClassifiedImage(iconPix2.toImage());
// ....additional operations....
}
I have been struggling with this problem for some days now and anyone who can shed some light on this or provide a solution on how to solve this would be great.
I am not sure if this is still useful to you but since I had the same problem this might be helpful. I think you are never passing the image to the destination table. Are you ever loading the image? Are you ever doing something like this:
clipscene.h
private:
QList<QGraphicsPixmapItem*> leftClipPix;
clipscene.cpp
void clipScene::load_classifiedImageFromDb(QImage image)
{
clasImg = image;
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
leftClipPix.append(item);
workingImageScene->addItem(item);
ui->yourgraphicsView->show();
ui->yourgraphicsView->setSceneRect(QRectF(0, 0, image.width(), image.height()));
}
Also if, as you said, you are trying to upload an image from your Desktop or database, than it may be useful to do something like:
void clipScene::load_classifiedImage()
{
QString dir = QFileDialog::getOpenFileName(this, tr("Open image directory"), "", tr("Images (*.tif *.jpg *.png *.jpeg *.bmp *.gif *.tiff)"));
QImage image;
if(QString::compare(dir, QString()) != 0) {
image = QImage(dir);
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
leftClipPix.append(item);
workingImageScene->addItem(item);
}
ui->yourgraphicsView->show();
ui->yourgraphicsView->setSceneRect(QRectF(0, 0, image.width(), image.height()));
clasImg = image;
}
and pass load_classifiedImage() function to a QPushButton
I've tried this:
QBrush brush(QPixmap(":/new/prefix1/car.jpg"));
painter.setBrush(brush);
QRectF car(positions[i],120, 20, 10 );
painter.drawRect(car);
It shows the image but it repeats itself during a simulation in QPaint.
I want a way to fill a rectangle with an image but i'm not finding any specific methods for Rect. Any tricks for that?
Use QPainters drawPixmap. There is an overloaded function, that takes both a QPixmap and a QRect into which the pixmap will be painted:
http://doc.qt.io/qt-5/qpainter.html#drawPixmap-9
I have a QLabel called Picture which Pixmap has been set to a QImage.
I now want to modify the image in another function.
Is it possible to get the Pixmapfrom the Picture?
I tried the following, which results in an unhandled exception:
QPixmap const *pm = ui.Picture->pixmap();
QImage image(pm->toImage());
Is there any other way?
I think you shall check the return value of the pixmap() function.
const QPixmap* pixmap = ui.Picture->pixmap();
if ( pixmap )
{
QImage image( pixmap->toImage() );
}
Maybe you just called the function when the image was not set and that caused the exception.
I am trying to draw line to QImage and show it in Qlabel. However I have some issues that I cannot solve.
QPixmap px;
px.fromImage (imgRaw); // define in header file QImage imgRaw;
QPainter p (&px);
p.setPen (Qt::red);
p.drawLine (mouseStart_X, mouseStart_Y, mouseReleased_X, mouseReleased_Y);
p.end ();
ui->lblRightImg->setPixmap (px);
ui->lblRightImg->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
ui->lblRightImg->setScaledContents(true);
When I used this code above it gives such error :
QPainter::begin: Paint device returned engine == 0, type: 2
QPainter::setPen: Painter not active
QPainter::end: Painter not active, aborted
Then I change my code because it tries to draw in null pixmap so after changing code like this:
QPixmap px(100, 100);
px.fromImage (imgRaw); // define in header file QImage imgRaw;
Then it gives noisy image(black and gray broken image)
Could you please help me to solve this issue ?
EDIT :
Also tried:
QPixmap px = QPixmap::fromImage (imgRaw);
Then it gives same image without any drawing..
fromImage is a static function of QPixmap and does not affect your 'object', it returns the pixmap you want. Try using following code to initialize your pixmap:
QPixmap px = QPixmap::fromImage(imgRaw);
I'm new in qt and base on my research i can change the size of a pixmap/image using the scaled function. but when i run the program the image size is still the same. here is the code.
QStringList headers;
QString headerValues="header1,header2";
headers=headerValues.split(",");
ui->tableWidget_2->setColumnCount(2);
ui->tableWidget_2->setHorizontalHeaderLabels(headers);
QStringList verticalHeaders;
QString verticalHeaderValues="vertical1,vertical2";
verticalHeaders=verticalHeaderValues.split(",");
ui->tableWidget_2->setRowCount(1);
ui->tableWidget_2->setVerticalHeaderLabels(verticalHeaders );
ui->tableWidget_2->horizontalHeader()->setDefaultSectionSize(150);
ui->tableWidget_2->verticalHeader()->setDefaultSectionSize(150);
ui->tableWidget_2->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
ui->tableWidget_2->verticalHeader()->setResizeMode(QHeaderView::Fixed);
QString filename="/home/marcvincento/Desktop/Private/Projects/fixButtons/Printer.png";
QPixmap pic(filename);
pic.scaled ( 20, 20, Qt::IgnoreAspectRatio, Qt::FastTransformation );
QBrush brush(pic);
QTableWidgetItem* item=new QTableWidgetItem();
item->setBackground(brush);
item->setText("ADD 5,1");
ui->tableWidget_2->setItem(0,0,item);
If you read the Qt documentation of QPixmap, you will see that the scaled() functions are declared const and return a QPixmap. That means the pixmap itself is not modified. What happens is that these functions return a scaled version of the pixmap. You will have to store that somewhere. In your case, you can just pass the scaled pixmap to the QBrush constructor:
QPixmap pic(filename);
QBrush brush(pic.scaled(20, 20, Qt::IgnoreAspectRatio,
Qt::FastTransformation));
If you would need to use the same scaled pixmap again later on and have no use for the original, unscaled pixmap, then you should construct the pic pixmap directly from a scaled version of the source, so that you won't end up performing the same scaling operation multiple times:
QPixmap pic(QPixmap(filename).scaled(20, 20, Qt::IgnoreAspectRatio,
Qt::FastTransformation));
If you do need both the scaled and unscaled versions, then obviously you'd need two pixmaps; one of the original and one for the scaled version:
QPixmap pic(filename);
QPixmap picScaled(pic.scaled(20, 20, Qt::IgnoreAspectRatio,
Qt::FastTransformation));