How to print to PNG format with Qt4 - c++

I'm struggling to print an image to the PNG format using Qt4.
The code below has default settings of either PDF or PS, but no way to choose PNG:
void DetectorView::printToFile()
{
// A basic printing function
QPrinter printer;
QPrintDialog dialog(&printer, this);
if (dialog.exec()==QDialog::Accepted) {
QPainter painter(&printer);
this->render(&painter);
std::cout << "INFO [DetectorView::printToFile] Wrote file " << std::endl;
}
else {
std::cout << "INFO [DetectorView::printToFile] Cancelling printer " << std::endl;
}
}
Any help would be appreciated!

Using this link: Rendering QWidget to QImage loses alpha-channel, you can render your widget to a QImage.
Then, using QImageWriter, you can save it to a png:
// render QWidget to QImage:
QImage bitmap(this->size(), QImage::Format_ARGB32);
bitmap.fill(Qt::transparent);
QPainter painter(&bitmap);
this->render(&painter, QPoint(), QRegion(), QWidget::DrawChildren);
// save QImage to png file:
QImageWriter writer("file.png", "png");
writer.write(bitmap);
Note: links provided are for Qt5, but this should work with Qt4.

Using jpo38's answer, I expanded to get the behaviour I wanted:
void DetectorView::printToFile()
{
QString default_name = "myImage.png";
QImage bitmap(this->size(), QImage::Format_ARGB32);
QPainter painter(&bitmap);
this->render(&painter,QPoint(),QRegion(),QWidget::DrawChildren);
QString filename = QFileDialog::getSaveFileName(this, tr("Save File"),QDir::homePath()+"/"+default_name,tr("Image Files (*.png *.jpg *.bmp)"));
QImageWriter writer(filename, "png");
writer.write(bitmap);
std::cout << "INFO [DetectorView::printToFile] Wrote image to file" << std::endl;
}
Note the QFileDialog which is needed to create the interactive window.

Related

QTextDocument::DrawContents skips resources?

I have this setup:
// ...
// variable document is a QTextDocument* which has some 'RichText' + 'Images'
QTextEdit textEdit;
textEdit.setDocument(document);
textEdit.setLineWrapMode(QTextEdit::LineWrapMode::NoWrap);
auto image = QImage(document->size().width(), document->size().height(),
QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter painter(&image);
document->drawContents(&painter);
// ...
I'm doing this to have my text rendered in a long horizontal QImage (hence the "NoWrap" LineWrapMode), so I can select a small part of it at a time with QImage::copy(QRect) and create a scrolling text effect.
The reason I'm doing it this way is that I need to have a QImage at the end which then I would feed its buffer (QImage::bits()) to the hardware that I'm using as my final output.
So it works great, it displays formatted text with fonts and colors and everything except for the images, it seems to skip them, notice the file icon in "result of text with image" picture.
This is text only in editor
This is result of text only
This is text with image in editor
This is result of text with image
This is how I'm inserting images to my QTextDocument:
QImage image(url.toLocalFile());
if (image.isNull())
return;
image = image.scaledToHeight(getDocumentHeight(), Qt::SmoothTransformation);
auto filename = QUrl(url.fileName());
textEdit->document()->addResource(QTextDocument::ImageResource, filename, image);
textEdit->textCursor().insertImage(filename);
So I don't think it's because "DrawContents" fails to find the image resource file or something like this.
What should I do? Is there something that I'm missing? Any kind of help in the matter is highly appreciated! ;)
In the following code I show how an image should be loaded, then save it to a file, probably the error is that you have not finished painting, for this you must call painter.end() or delete painter from memory.
main.cpp
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
QVBoxLayout vlayout(&widget);
QTextEdit textEdit;
QPushButton button("save image");
QPushButton loadButton("Load and Insert");
vlayout.addWidget(&loadButton);
vlayout.addWidget(&textEdit);
vlayout.addWidget(&button);
widget.show();
textEdit.append("some text");
QObject::connect(&loadButton, &QPushButton::clicked,[&textEdit](){
QString filename = QFileDialog::getOpenFileName(&textEdit, "Select", "", "*.png");
if(!filename.isEmpty()){
QImage image(filename);
QUrl url = QUrl::fromLocalFile(filename);
image = image.scaledToHeight(100, Qt::SmoothTransformation);
textEdit.document()->addResource(QTextDocument::ImageResource, url, image);
textEdit.textCursor().insertImage(image);
}
});
QObject::connect(&button, &QPushButton::clicked, [&textEdit](){
QImage image(textEdit.document()->size().toSize() , QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter painter(&image);
textEdit.document()->drawContents(&painter);
painter.end();
image.save("image.png");
});
return a.exec();
}

Loading sf::Image by QDialog

Is there a easy way to load sf::Image using for example QDialog or other library? I mean I want to load image for using it in sfml by "Choose image..." window.
You can use QFileDialog to get the path of the given image:
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
QDir::currentDirPath() ,
tr("Images (*.png *.xpm *.jpg)"));
if (!fileName.isEmpty()) {
// Now you can init your sf::image
}
According to the official documentation of SFML, you can use the loadFromFile function
sf::Image image;
if (image.loadFromFile(filename.toStdString())) {
std::cout << "image loaded with success";
}

C++ (Qt5.5) expend image Viewer example by save function

I expanded the Qt Imageviewer example by some functionality. I basically want to add a save function. In this example there are two functions of the same class handling the picture open process:
void ImageViewer::open()
{
QStringList mimeTypeFilters;
foreach (const QByteArray &mimeTypeName, QImageReader::supportedMimeTypes())
mimeTypeFilters.append(mimeTypeName);
mimeTypeFilters.sort();
const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
QFileDialog dialog(this, tr("Open File"),
picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last());
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setMimeTypeFilters(mimeTypeFilters);
dialog.selectMimeTypeFilter("image/jpeg");
while (dialog.exec() == QDialog::Accepted && !loadFile(dialog.selectedFiles().first())) {}
}
and
bool ImageViewer::loadFile(const QString &fileName)
{
QImageReader reader(fileName);
reader.setAutoTransform(true);
const QImage image = reader.read();
if (image.isNull()) {
QMessageBox::information(this, QGuiApplication::applicationDisplayName(),
tr("Cannot load %1.").arg(QDir::toNativeSeparators(fileName)));
setWindowFilePath(QString());
imageLabel->setPixmap(QPixmap());
imageLabel->adjustSize();
return false;
}
imageLabel->setPixmap(QPixmap::fromImage(image));
scaleFactor = 1.0;
printAct->setEnabled(true);
fitToWindowAct->setEnabled(true);
convAct->setEnabled(true); // so the image can be converted if it was loaded ...
updateActions();
if (!fitToWindowAct->isChecked()) {
imageLabel->adjustSize();
}
setWindowFilePath(fileName);
return true;
}
So I added a save button in the menus, and in the ImageViewer.h class:
class ImageViewer : public QMainWindow
{
Q_OBJECT
public:
ImageViewer();
bool loadFile(const QString &);
private slots:
void open();
void print();
void save(); // <---
Everything is fine, but I don't know how to get my Image in the new function, besides the fact, that I obviously make a wrong conversion from QPixmap to QImage - but I also tried replacing it with QPixmap test = imageLabel->pixmap() without any success.
void ImageViewer::save()
{
QImage test = imageLabel->pixmap();
qWarning()<< test;
QByteArray bytes;
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "BMP");
QString monobitmap = QString::fromLatin1(bytes.toBase64().data());
}
In the end, I want to save it as a monochrome bitmap (no matter what it was before). Sorry for posting a lot of code.
It sounds like your problem is that you have a QPixmap object and you need a QImage object. If that's the case, then you can convert a QPixmap into a QImage by calling the toImage() method on the QPixmap; it will return the resulting QImage object.
As for you converting the QImage to a monochrome bitmap, you should be able to do that by calling convertToFormat(QImage::Format_Mono) on your QImage. That call will return the new (1-bit) version of the QImage.

How to print multiple instances of QTextBrowser into one PDF file?

the QT application I'm working on comes with a tutorial. Each chapter is a stand-alone HTML file, each file can span multiple pages. Now I want to print them into one single PDF file (with page numbers).
My naive approach was this, but it's wrong:
#include <QApplication>
#include <QPrinter>
#include <QTextBrowser>
#include <QUrl>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPrinter printer;
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOutputFileName("/tmp/test.pdf");
QTextBrowser *tp = new QTextBrowser();
tp->setSource(QUrl("qrc:///help/tutorial_item_1.html"));
tp->print(&printer);
tp->setSource(QUrl("qrc:///help/tutorial_item_2.html"));
tp->print(&printer);
tp->setSource(QUrl("qrc:///help/tutorial_item_3.html"));
tp->print(&printer);
// etc...
}
However, this will restart the printer on each print() call, starting with a new PDF file, overwriting the old one.
What is a simple solution to print all HTML into one PDF file, using QT?
Developping on your "naive approach", I could print concatenated html files by appending several pages to a parent QTextEdit. It would probably also work utilizing a second QTextBrowser instead.
// ...
QTextBrowser *tp = new QTextBrowser();
QTextEdit te;
tp->setSource(QUrl("qrc:///help/tutorial_item_1.html"));
te.append(tp->toHtml());
tp->setSource(QUrl("qrc:///help/tutorial_item_2.html"));
te.append(tp->toHtml());
te.print(&printer);
// ...
You can achieve this by rendering your contents on a QPainter object linked to the QPrinter device
// Sample code ahead ~>
QPrinter printer;
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOutputFileName("C:\\test.pdf");
printer.setFullPage(true);
printer.setPageSize(QPrinter::A4);
QTextBrowser tb;
QPainter painter;
painter.begin(&printer);
QRect rect = printer.pageRect();
tb.resize(rect.width(), rect.height());
{
QFile file("C:\\test1.html");
if(file.open(QIODevice::ReadOnly)) {
QTextStream ts(&file);
tb.setHtml(ts.readAll());
file.close();
tb.render(&painter, QPoint(0,0));
}
}
if(printer.newPage() == false)
qDebug() << "ERROR";
{
QFile file("C:\\test2.html");
if(file.open(QIODevice::ReadOnly)) {
QTextStream ts(&file);
tb.setHtml(ts.readAll());
file.close();
tb.render(&painter, QPoint(0,0));
}
}
painter.end();

when the window of QFileDialog::getOpenFileName opening, the program has unexpected finished

I'm writing a simple Qt application to test multi-threading (something I am also completely new to).I made a QApplication to manage the GUI,then I write a class VisionApp that contains the class MainWindow which is a subclass of QMainWindow.
In the MainWindow class,I write a function void MainWindow::getfromfilevd() which is connected to the button using this:
QObject::connect(ui->FileVdButton,SIGNAL(clicked()),this,SLOT(getfromfilevd()));
Then I want to read a image from file by using QFileDialog::getOpenFileName,my code is here:
void MainWindow::getfromfilevd()
{
//mutex.lock();
from_imgorvd = true;
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"),"", tr("Image Files (*.png *.jpg *.bmp *.xpm)"));
if(fileName.isEmpty()) {
cv::Mat image;
image = cv::imread(fileName.toUtf8().constData(), CV_LOAD_IMAGE_COLOR);
mutex.lock();
Mat_Img = image.clone();
mutex.unlock();
}
}
however,every time I click the button ,the window of QFileDialog opened but it is blank,then my program finished unexpected.
when I use this code:
void MainWindow::getfromfilevd()
{
from_imgorvd = true;
cv::Mat image;
image = cv::imread("/home/somnus/Picture/mouse.jpg", CV_LOAD_IMAGE_COLOR);
if(! image.data) {
std::cout << "Could not open or find the image" << std::endl ;
}
else {
mutex.lock();
Mat_Img = image.clone();
mutex.unlock();
}
}
It works well.
I am really wonder which mistake I take...
Hope for your help
It should be like this, !fileName.isEmpty() in stead of fileName.isEmpty() because you need to load image when the file name is not empty not the opposite.