How to print multiple tables into one pdf - qt

I'm trying to print multiple table (qtablewidget) objects in single pdf using qt.
I can print one table, using the code provided in(https://forum.qt.io/topic/80501/qpainter-howto-draw-table/7)
QPixmap pix(widget->size());
QPainter painter(&pix);
widget->render(&painter);
painter.end();
QPrinter printer(QPrinter::HighResolution);
printer.setOrientation(QPrinter::Landscape);
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setPaperSize(QPrinter::A4);
printer.setOutputFileName("test.pdf"); // will be in build folder
painter.begin(&printer);
painter.drawPixmap(0, 0, pix);
painter.end();
However, if I try to print multiple tables, the code fails. If I create multiple QPainters, qt just outputs multiple pdfs, with one table in each pdf. I'm trying to do it using one QPainter and multiple QPixmaps, but no success so far.
Would anyone please let me know how I can get around it?
Any help would be appreciated
Regards,

How does the code fail? The below should work (I didn't test it). Note the absence of manual object lifetime management: let the compiler do it for you. A QPainter is a proper C++ class and knows how to release its resources without having to manually invoke QPainter::end().
void printWidgets(QWidgetList widgets) {
QVector<QPixmap> pixmaps;
for (auto *w : widgets) {
QPixmap pix(w->size());
QPainter painter(pix);
w->render(&painter);
pixmaps.push_back(pix);
}
QPrinter printer(QPrinter::HighResolution);
printer.setOrientation(QPrinter::Landscape);
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setPaperSize(QPrinter::A4);
printer.setOutputFileName("test.pdf"); // will be in build folder
QPainter painter(&printer);
QPoint pos;
for (auto &pix : qAsConst(pixmaps)) {
painter.drawPixmap(pos, pix);
pos.ry() += pix.height(); // stack the output vertically
}
}

Related

Using QGLFramebufferObject and shaders without QGLWidget

I want to create an OGL data processor using QGLFunctions shaders and framebuffers. I don't need any widgets. But to create valid Shader and framebuffer instances, I need a valid QGLContext with support for the appropriate glExtensions.
With zero context, of course, nothing works. With context of zero QPaintDevice too. With Qpixmap as device it creates a valid context, but it lacks glExtensions for Shader and framebuffer.
#include <QGLFramebufferObject>
#include <QGLShaderProgram>
#include <QtOpenGL/QGLFunctions>
// ...
void GLProcessor::init()
{
auto format = QGLFormat::defaultFormat();
if (!context()){
m_context = new QGLContext(format, new QPixmap(1, 1));
bool ok = m_context->create();
qDebug() << "CREATING CONTEXT "<< ok;
Q_ASSERT(context()->isValid());
}
context()->makeCurrent();
initializeGLFunctions(context());
m_binFBO = new QGLFramebufferObject(lowsize ,lowsize ,QGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RED);
m_outFBO = new QGLFramebufferObject(lowsize ,1 ,QGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RED);
setupShaders();
// ...
}
There is an option, of course, to do as always is to get the context from the QGLWidget and hide it. But somehow inelegant. PS CUDA, OpenCL, AMP and so on I don't need. For my tasks need OpenGL.
How do I use shaders and framebuffers in qt4 without creating a QGLWidget?

Extracting icon using WinAPI in Qt app

I'm trying to extract icon from exe file using WinAPI, but it doesn't work.
Here's the code:
QIcon OSTools::AppsInterface::extractAppIcon(const QString &fileName) const {
wchar_t *convertedName = new wchar_t[fileName.length() + 1];
fileName.toWCharArray(convertedName);
convertedName[fileName.length()] = '\0';
HICON Icon = ExtractIcon(NULL, convertedName, 0);
QPixmap pixmap = QPixmap::fromWinHICON(Icon);
return QIcon(pixmap);
}
Code outputs:
QPixmap::fromWinHICON(), failed to GetIconInfo()
(ExtractIcon function on MSDN).
I think problem is that I send NULL instead of "A handle to the instance of the application calling the function". But, generally, I use Qt, and it's only one WinAPI function in my app.
What's wrong? What's correct way to extract icon using WinAPI? If you have another function proposal, please, give me an example. This is the first time I'm using WinAPI.
UPDATE: Yes, there is a better way. You may use QFileIconProvider class for doing such things.
Works for me, even with NULL. But obtaining the HINSTANCE is actually very simple. You have a problem elsewhere i guess. Does your target exe really have an embedded icon?
#ifdef Q_WS_WIN
#include <qt_windows.h>
#endif
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
#ifdef Q_WS_WIN
QString fileName("D:\\_dev\\eclipse\\eclipse.exe");
wchar_t *convertedName = new wchar_t[fileName.length() + 1];
fileName.toWCharArray(convertedName);
convertedName[fileName.length()] = '\0';
HINSTANCE hInstance = ::GetModuleHandle(NULL);
HICON Icon = ::ExtractIcon(hInstance, convertedName, 0);
ui->label->setPixmap(QPixmap::fromWinHICON(Icon));
#endif
}
I used QFileIconProvider, and it worked perfectly. Try this :
QPushButton b;
b.show();
QIcon icon;
QFileIconProvider fileiconpr;
icon = fileIconProvider.icon(QFileInfo("/*file name*/"));
b.setIcon(icon);
// And you can also save it where you want :
QPixmap pixmap = icon.pixmap( QSize(/*desired size*/) );
pixmap.save("/Desktop/notepad-icon.png");
Source. Have a nice day.
And solution was very simple. I just sent path to '.lnk' file instead of path to file. That's my inattention.

Problem with QT save() function for QImage

I have a problem which I have been trying to figure out for few days now. I am using OpenCV to get frames from a camera and then I am converting it to QImage and trying to save the frame on a disk. The problem I am facing is that if I compile/run the code in release/debug mode in visual studio then it works fine. If I run the executable created by visual studio for debug mode then it runs fine. But if I run the executable created by visual studio in release mode, the gui works well except that the QImage save() function doesn't work anymore. If I use OpenCV function for saving the frame then it works well in all modes. The QImage that I am trying to save is notNull because my comments text box displays the line I print inside but the save function returns false. I would really appreciate help.
Here is a little code:
Code for conversion between IplImage and QImage (I have also used another code, with the same result)
QImage A::IplImage2QImage(const IplImage *iplImage)
{
int height = iplImage->height;
int width = iplImage->width;
if (iplImage->depth == IPL_DEPTH_8U && iplImage->nChannels == 3) {
const uchar *qImageBuffer = (const uchar*)iplImage->imageData;
QImage img(qImageBuffer, width, height, QImage::Format_RGB888);
return img.rgbSwapped();
}
else {
// qWarning() << "Image cannot be converted.";
return QImage();
}
}
void A::getFrame()
{
commentsTextBox->setText("getFrame");
if (VI.isFrameNew(device1)) {
VI.getPixels(device1, (unsigned char *)frame->imageData, false, true);
}
QImage qimg2 = IplImage2QImage(frame);
m_Label->setPixmap(QPixmap::fromImage(qimg2));
if (!qimg2.isNull()) {
x = qimg2.save("C:\\Data\\test.jpg");
commentsTextBox->append("notEmpty33");
}
}
I had the same problem to QImage::load(). I will post a copy of the solution pointed out in the link of alexisdm, in order to make it clearer.
Under windows the qjpeg4.dll must in a imageformats directory under
the application's directory by default.
Thank you!
Try something like this,
// Prepare the url,
QUrl url("file:///home/ubuntu/Desktop/image.jpg");
// Get the path,
QString path = url.path();
// Save the image,
myQimage.save(path);

How to draw QGLFrameBufferObject onto the painter from within QGraphicsItem::paint()

Small version of my question
In a QGraphicsItem::paint() function I have a QGLFrameBufferObject. How do I get it on the paintdevice of the painter that is passed as an argument? (provided that the QGraphicsItem is in a scene that is being rendered by a QGraphicsView that has a QGLWidget as viewport => painter is using opengl engine)
QGraphicsItem::paint(QPainter* painter, ...)
{
QGLFramebufferObject fbo;
QPainter p(fbo);
... // Some painting code on the fbo
p.end();
// What now? How to get the fbo content drawn on the painter?
}
I have looked at the framebufferobject and pbuffer examples provided with Qt. There the fbo/pbuffer is drawn in a QGLWidget using custom opengl code. Is it possible to do the same thing within a paint() method of a QGraphicsItem and take the position of the QGraphisItem in the scene/view into account?
Big version of my question
Situation sketch
I have a QGraphicsScene. In it is an item that has a QGraphicsEffect (own implementation by overriding draw() from QGraphicsEffect). The scene is rendered by a QGraphicsView that has a QGLWidget as viewport.
In the QGraphicsEffect::draw(QPainter*) I have to generate some pixmap which I then want to draw using the painter provided (the painter has the QGLWidget as paintdevice). Constructing the pixmap is a combination of some draw calls and I want these to be done in hardware.
Simplified example: (I don't call sourcepixmap in my draw() method as it is not needed to demonstrate my problem)
class OwnGraphicsEffect: public QGraphicsEffect
{
virtual void draw(QPainter* painter);
}
void OwnGraphicsEffect::draw(QPainter* painter)
{
QRect rect(0,0,100,100);
QGLPixelBuffer pbuffer(rect.size(), QGLFormat(QGL::Rgba));
QPainter p(pbuffer);
p.fillRect(rect, Qt::transparent);
p.end();
painter->drawImage(QPoint(0,0), pbuffer->toImage(),rect);
}
Actual problem
My concerns are with the last line of my code: pbuffer->toImage(). I don't want to use this. I don't want to have a QImage conversion because of performance reasons. Is there a way to get a pixmap from my glpixelbuffer and then use painter->drawpixmap()?
I know I also can copy the pbuffer to a texture by using :
GLuint dynamicTexture = pbuffer.generateDynamicTexture();
pbuffer.updateDynamicTexture(dynamicTexture);
but I have no idea on how to get this texture onto the "painter".
Extending leemes' answer, here is a solution which can also handle multisample framebuffer objects.
First, if you want to draw on a QGLWidget, you can simply use the OpenGL commands
leemes suggested in his answer. Note that there is a ready-to-use drawTexture()
command available, which simplifies this code to the following:
void Widget::drawFBO(QPainter &painter, QGLFramebufferObject &fbo, QRect target)
{
painter.beginNativePainting();
drawTexture(target, fbo.texture());
painter.endNativePainting();
}
To draw multisample FBOs, you can convert them into non-multisample ones
using QGLFramebufferObject::blitFramebuffer (Note that not every hardware
/ driver combination supports this feature!):
if(fbo.format().samples() > 1)
{
QGLFramebufferObject texture(fbo.size()); // the non-multisampled fbo
QGLFramebufferObject::blitFramebuffer(
&texture, QRect(0, 0, fbo.width(), fbo.height()),
&fbo, QRect(0, 0, fbo.width(), fbo.height()));
drawTexture(targetRect, texture.texture());
}
else
drawTexture(targetRect, fbo.texture());
However, as far as I know, you can't draw using OpenGL commands on a non-OpenGL context.
For this, you first need to convert the framebuffer to a (software) image, like
a QImage using fbo.toImage() and draw this using your QPainter instead of the
fbo directly.
I think I figured it out. I use the QPainter::beginNativePainting() to mix OpenGL commands in a paintEvent:
void Widget::drawFBO(QPainter &painter, QGLFramebufferObject &fbo, QRect target)
{
painter.beginNativePainting();
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBindTexture(GL_TEXTURE_2D, fbo.texture());
glBegin(GL_QUADS);
glTexCoord2d(0.0,1.0); glVertex2d(target.left(), target.top());
glTexCoord2d(1.0,1.0); glVertex2d(target.right() + 1, target.top());
glTexCoord2d(1.0,0.0); glVertex2d(target.right() + 1, target.bottom() + 1);
glTexCoord2d(0.0,0.0); glVertex2d(target.left(), target.bottom() + 1);
glEnd();
painter.endNativePainting();
}
I hope this will also work in the paintEvent of a QGraphicsItem (where the QGraphicsView uses a QGLWidget as the viewport), since I only tested it in QGLWidget::paintEvent directly.
There is, however, still the following problem: I don't know how to paint a multisample framebuffer. The documentation of QGLFramebufferObject::texture() says:
If a multisample framebuffer object is used then the value returned from this function will be invalid.

OpenNI + OpenCV + Qt

I'm trying to make an app using Kinect (OpenNI), processing the image (OpenCV) with a GUI.
I tested de OpenNI+OpenCV and OpenCV+Qt
Normally when we use OpenCV+Qt we can make a QWidget to show the content of the camera (VideoCapture) .. Capture a frame and update this querying for new frames to device.
With OpenNI and OpenCV i see examples using a for cycle to pull data from Kinect Sensors (image, depth) , but i don't know how to make this pulling routing mora straightforward. I mean, similar to the OpenCV frame querying.
The idea is embed in a QWidget the images captured from Kinect. The QWidget will have (for now) 2 buttons "Start Kinect" and "Quit" ..and below the Painting section to show the data captured.
Any thoughs?
You can try the QTimer class to query the kinect at fixed time intervals. In my application I use the code below.
void UpperBodyGestures::refreshUsingTimer()
{
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(MainEventFunction()));
timer->start(30);
}
void UpperBodyGestures::on_pushButton_Kinect_clicked()
{
InitKinect();
ui.pushButton_Kinect->setEnabled(false);
}
// modify the main function to call refreshUsingTimer function
UpperBodyGestures w;
w.show();
w.refreshUsingTimer();
return a.exec();
Then to query the frame you can use the label widget. I'm posting an example code below:
// Query the depth data from Openni
const XnDepthPixel* pDepth = depthMD.Data();
// Convert it to opencv for manipulation etc
cv::Mat DepthBuf(480,640,CV_16UC1,(unsigned char*)g_Depth);
// Normalize Depth image to 0-255 range (cant remember max range number so assuming it as 10k)
DepthBuf = DepthBuf / 10000 *255;
DepthBuf.convertTo(DepthBuf,CV_8UC1);
// Convert opencv image to a Qimage object
QImage qimage((const unsigned char*)DepthBuf.data, DepthBuf.size().width, DepthBuf.size().height, DepthBuf.step, QImage::Format_RGB888);
// Display the Qimage in the defined mylabel object
ui.myLabel->setPixmap(pixmap.fromImage(qimage,0).scaled(QSize(300,300), Qt::KeepAspectRatio, Qt::FastTransformation));

Resources