Can not print to paper in Qt - qt

I can not print to paper for some reasone. So I have a functional printer. And I use the folowing code to print a qDialog and a few pictures out:
QPrinter printer;
QPainter painter;
painter.begin(&printer);
double xscale = printer.width() / double(window->width());
double yscale = printer.height() / double(window->height());
double scale = qMin(xscale, yscale);
painter.scale(scale, scale);
QPrintDialog printDialog(&printer, this);
if (printDialog.exec() == QDialog::Accepted) {
bool skip = true;
if(ui->generalInfos->isChecked()) {
//window is a QDialog I want to print out
window->render(&painter);
skip = false;
}
QList<Document *> docs;
if(worker) {
//a list with path to pictures
docs = worker->getDocuments();
}
for(auto document : docs) {
if(ui->Documents->isChecked(document->getID())) {
for(auto scan : document->getScans()) {
if(!skip) {
printer.newPage();
}
else {
skip = false;
}
painter.resetTransform();
const QImage image(scan);
const QPoint imageCoordinates(0,0);
xscale = printer.width() / double(image.width());
yscale = printer.height() / double(image.height());
scale = qMin(xscale, yscale);
painter.scale(scale, scale);
painter.drawImage(imageCoordinates,image);
}
}
}
}
painter.end();
and it doesn't work. Nothing is printed and Qt trows an error:
QWin32PrintEngine::newPage: EndPage failed (The parameter is incorrect.)
QWin32PrintEngine::end: EndPage failed (0x31210cf7) (The parameter is incorrect.)
can someone please help me?

If you simplify your code, you will probably find the solution.
So lets start with selecting the printer, then (afterwards!) start painting to the printer:
QPrinter printer;
QPrintDialog printDialog(&printer, this);
if (printDialog.exec() == QDialog::Accepted)
{
QPainter painter;
painter.begin(&printer);
window->render(&painter);
painter.end();
}
If this works, add more of your old code to the sketch above.
If it doesn't work, something else in your program or your environment (selected printer?) is wrong, so you need to extend your bug hunt beyond what you showed us here.

Related

Programmatic scrolling with QGraphicsView and QGraphicsItem?

I would like to programmatically scroll a scene to the left / right, but I am not sure how to do that properly. Note that I do not want to have (visible) scroll bars.
I use a standard QGraphicsView + QGraphicsScene + QGraphicsItem setup. I have downsized it to the minimum, with one single QGraphicsItem (a QGraphicsRectItem) in the scene.
I have managed to achieve programmatic scrolling by setting my view like this:
// view setup
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
and then, in another part of the code:
// programmatic scrolling
QScrollBar* const sb = view->horizontalScrollBar();
sb->setRange(0, 1000); // some values for experimenting
sb->setValue(sb->value() + 100 or -100); // some increment for experimenting
This works, but... scrolling through invisible scrollbars doesn't feel right.
I tried this more straightforward approach:
// programmatic scrolling - doesn't quite work
view->viewport()->scroll(100 or -100, 0); // some increment for experimenting
This code does scroll, but when the rectangle goes off the left edge of the view, and I reverse the scrolling direction (increment changed from 100 to -100 in the call to scroll()), the uncovered part of the rectangle is not repainted. The reason is that QGraphicsRectItem::paint() is not called in that case (it is called when using the scrollbar method).
So, is there a way to get viewport()->scroll() work? Or some other simple way to achieve programmatic scrolling? Or is the artificial scrollbar method just the way to go?
Moving the view assumes that it's smaller than its scene. If they're the same size, it won't move.
QGraphicsView can be set to centerOn any position in scene coordinates. Use a timer to call centerOn to move the view one frame at a time.
Here's a working example: -
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QTimer>
class MyView : public QGraphicsView
{
private:
public:
MyView(QGraphicsScene* pScene)
: QGraphicsView(pScene, NULL)
{}
void AnimateBy(int x)
{
float updateFrequency = (1000/30.0); // ~30 frames per second
QPointF currScenePos = sceneRect().center();
int curX = currScenePos.x();
int endPos = curX + x;
int distanceToAnimate = (endPos - curX);
// speed = dist / time
float updatePosInterval = (float)distanceToAnimate / updateFrequency;
printf("updatePosInterval: %f \n", updatePosInterval);
static float newXPos = sceneRect().center().x();
QTimer* pTimer = new QTimer;
QObject::connect(pTimer, &QTimer::timeout, [=](){
newXPos += updatePosInterval;
centerOn(newXPos, sceneRect().center().y());
// check for end position or time, then....
if(newXPos >= endPos)
{
pTimer->stop();
pTimer->deleteLater();
}
});
pTimer->start(updateFrequency);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene(0, 0, 10000, 20000);
MyView* view = new MyView(&scene);
QGraphicsRectItem* pRect = new QGraphicsRectItem(0, 0, 100, 100);
pRect->setPos(scene.width()/2, scene.height()/2);
scene.addItem(pRect);
// timer to wait for the window to appear, before starting to move
QTimer* pTimer = new QTimer;
pTimer->setSingleShot(true);
QObject::connect(pTimer, &QTimer::timeout,[=](){
view->centerOn(pRect); // centre on the rectangle
view->AnimateBy(100);
pTimer->deleteLater();
});
pTimer->start(1000);
view->show();
return a.exec();
}
So, we create the animation by moving the view frame-by-frame using the call to centerOn.
For simplicity, the code just deals with moving in one axis. To move in 2 axis, use 2D vector maths to calculate the interval position.
Try to change the view transformation with the QGraphicsView::translate() or QGraphicsView::setTransform().
But keep in mind that you can't move the viewport "outside" the scene, so make sure that your scene rectangle is large enough.
If I got your question correctly, there is a dojo classes library with such class as PanWebView that allow QWebView to scroll smoothly with mouse without any scrollbars. Take a look at sources. It supports panning and can be suitable for mobile apps, but maybe it'll help you too.
PanWebView class looks like this
#include <QWebView>
#include <QWebFrame>
#include <QMouseEvent>
#include <QApplication>
class PanWebView : public QWebView
{
Q_OBJECT
private:
bool pressed;
bool scrolling;
QPoint position;
QPoint offset;
QList<QEvent*> ignored;
public:
PanWebView(QWidget *parent = 0): QWebView(parent), pressed(false), scrolling(false) {
QWebFrame *frame = page()->mainFrame();
frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
}
protected:
void mousePressEvent(QMouseEvent *mouseEvent) {
if (ignored.removeAll(mouseEvent))
return QWebView::mousePressEvent(mouseEvent);
if (!pressed && !scrolling && mouseEvent->modifiers() == Qt::NoModifier)
if (mouseEvent->buttons() == Qt::LeftButton) {
pressed = true;
scrolling = false;
position = mouseEvent->pos();
QWebFrame *frame = page()->mainFrame();
int x = frame->evaluateJavaScript("window.scrollX").toInt();
int y = frame->evaluateJavaScript("window.scrollY").toInt();
offset = QPoint(x, y);
QApplication::setOverrideCursor(Qt::OpenHandCursor);
return;
}
return QWebView::mousePressEvent(mouseEvent);
}
void mouseReleaseEvent(QMouseEvent *mouseEvent) {
if (ignored.removeAll(mouseEvent))
return QWebView::mouseReleaseEvent(mouseEvent);
if (scrolling) {
pressed = false;
scrolling = false;
QApplication::restoreOverrideCursor();
return;
}
if (pressed) {
pressed = false;
scrolling = false;
QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,
position, Qt::LeftButton,
Qt::LeftButton, Qt::NoModifier);
QMouseEvent *event2 = new QMouseEvent(*mouseEvent);
ignored << event1;
ignored << event2;
QApplication::postEvent(this, event1);
QApplication::postEvent(this, event2);
QApplication::restoreOverrideCursor();
return;
}
return QWebView::mouseReleaseEvent(mouseEvent);
}
void mouseMoveEvent(QMouseEvent *mouseEvent) {
if (scrolling) {
QPoint delta = mouseEvent->pos() - position;
QPoint p = offset - delta;
QWebFrame *frame = page()->mainFrame();
frame- >evaluateJavaScript(QString("window.scrollTo(%1,%2);").arg(p.x()).arg(p.y()));
return;
}
if (pressed) {
pressed = false;
scrolling = true;
return;
}
return QWebView::mouseMoveEvent(mouseEvent);
}
};
And usage:
PanWebView web;
web.setUrl(QUrl("http://news.google.com"));
web.setWindowTitle("Web View - use mouse to drag and pan around");
web.show();
Also did you check this and this topics? I think it can be usefull.

QLabel won't update pixmap from inside function

I've tried absolutely everything I know, and I've come to the conclusion that this issue is over my head. I've tried running repaint(), update() and this->update(); and everything else that I could think of. Pixmap works outside of the function (in the constructor) but not inside a function. Here is code (only relevant is pasted, please indicate if you would like more):
myWidget.h
#define NUM_POINTERS 10
QLabel* pointerArray[NUM_POINTERS];
QPixmap circle;
QPixmap* triangle;
QPixmap* whitex;
int activePointer;
myWidget.cpp
activePointer = 0;
QPixmap circle (":/Resources/greencircle.png");
this->whitex = new QPixmap(":/Resources/white_x.png");
this->triangle = new QPixmap(":/Resources/redtriangle.png");
//create an array of pointers to the label1-10 objects
pointerArray[0] = ui->label1;
pointerArray[1] = ui->label2;
pointerArray[2] = ui->label3;
...
pointerArray[9] = ui->label10;
for (int i = 0;i < 10; i++)
{
pointerArray[i]->setPixmap(circle);
}
void myWidget::changeImage()
{
updatesEnabled();
if (activePointer < 10){
pointerArray[activePointer]->setPixmap(*this->whitex);
activePointer++;
update();
}
else{
printf("end of array\n");
fflush(stdout);
}
}
I get a row of circles printed where I want them, but I won't get any white Xs. The pixmap changes to the whitex, but it will not update. It does not crash, it continues adding to activePointer until the end of the array.
Thanks in advance.
Quick Edit: I have tried pointerArray[activePointer]->update(); with no luck.

program crashes with haarcascade_fullbody.xml

I am working with OpenCV for Qt.
I am working on doing a program which is able to detect several objects. So far I could make a face, eye and nose detector, but when I try to make a full body detection I get either totally wrong detections, no detections at all or the program crashes. For detecting the full body I just use the same code as for the other detections but with the haarcascade_fullbody.xml file. Is it not possible to use the same code? Why does it work for the other features and not for the full body?
I have also tried to implement a car detection using OpenCV's pretrained models from https://github.com/Itseez/opencv_extra/tree/master/testdata/cv/latentsvmdetector/models_VOC2007 but I get parsing errors.
Thanks in advance!
Code from MainWindow:
void MainWindow::on_btnFullBody_clicked()
{
WriteInLog("Full body detection requested");
QString xml = tr("%1/%2").arg(QApplication::applicationDirPath()).arg(FULL_BODY_FILE);
FeatureDetector detector(xml);
std::vector<QRect> rest;
float scaleFactor= 1.1f;
uint neighbours= 2;
bool ret = detector.DetectFeature(&mSelectedImage, rest, scaleFactor, neighbours);
if (!ret)
{
WriteInLog("No full body has been detected");
}
else
{
QVector<QRect> qRect = QVector<QRect>::fromStdVector(rest);
processedImage(qRect);
WriteInLog("Bodys detected: "+QString::number(qRect.size()));
}
}
Code from DetectFeature:
bool FeatureDetector::DetectFeature(QImage* image, std::vector<QRect> &returnList, float scaleFactor, uint neighbours)
{
returnList.clear();
bool ok = false;
qDebug() << "Starting...";
if (!image->isNull()) {
//Changing from QImage to matrix
QImage temp = image->copy();
cv::Mat res(temp.height(),temp.width(),CV_8UC3,(uchar*)temp.bits(),temp.bytesPerLine());
cv::Mat res_gray;
//Changing the image to grey scale an equalizing the result
cvtColor(res, res_gray,CV_BGR2GRAY);
cv::equalizeHist(res_gray,res_gray);
cv::CascadeClassifier detector;
std::vector< cv::Rect > featureVec;
bool retDetector=true; // detector.load("C:/Users/ansurbcn_2/Pictures/cara.jpg");
qDebug()<<mXmlFilePath;
if (!detector.load(mXmlFilePath.toLatin1().constData()))
{
qDebug() << "Error loading detector";
return false;
}
detector.detectMultiScale(res_gray, featureVec);
//detector.detectMultiScale(res_gray, featureVec, scaleFactor, neighbours, 18|9);
if (retDetector) {
qDebug() << "OK Detector";
}
else {
qDebug() << "Failed Detector";
}
for(size_t i=0; i<featureVec.size();i++)
{
cv::Rect oneFeature =featureVec[i];
QRect qrect(oneFeature.x, oneFeature.y, oneFeature.width, oneFeature.height);
returnList.push_back(qrect);
ok = true;
}
}
return ok;
}

QMargins in QSS

Is it possible to set a custom QMargins Q_PROPERTY via QSS? Does it follow the margins syntax? Maybe I missed something obvious, but I didn't find any specific reference for this usage.
It was already mentioned in the previous answer that there's no specific parsing method for QMargins, but the property data is set as QStringList so we can add converters to make QMargins variable accept QStringList object.
const char QCssCustomValue_Margin[] = "qmargins";
QStringList marginsToStringList(const QMargins &margins) {
return {QLatin1String(QCssCustomValue_Margin), QString::asprintf("%dpx", margins.left()),
QString::asprintf("%dpx", margins.top()), QString::asprintf("%dpx", margins.right()),
QString::asprintf("%dpx", margins.bottom())};
}
QMargins stringListToMargins(const QStringList &stringList) {
QMargins res;
if (stringList.size() == 2 &&
!stringList.front().compare(QLatin1String(QCssCustomValue_Margin), Qt::CaseInsensitive)) {
QStringList valueList = stringList.back().split(",");
QVector<int> x;
for (int i = 0; i < qMin(valueList.size(), 4); ++i) {
QString str = valueList.at(i).simplified();
if (str.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
str.chop(2);
}
bool isNum;
int num = str.toInt(&isNum);
if (isNum) {
x.push_back(num);
} else {
x.push_back(0);
}
}
res.setLeft(x[0]);
res.setTop(x[1]);
res.setRight(x[2]);
res.setBottom(x[3]);
}
return res;
}
Then you should register these two functions to QApplication, make sure the codes below run before the property is set when the stylesheet is loaded.
// Implement outside
Q_DECLARE_METATYPE(QMargins)
// Execute after QApplication is created
QMetaType::registerConverter<QStringList, QMargins>(stringListToMargins);
QMetaType::registerConverter<QMargins, QStringList>(marginsToStringList);
After such preparation, when you have a Q_PROPERTY like this in a custom widget,
Q_PROPERTY(QMargins margins READ margins WRITE setMargins NOTIFY marginsChanged)
you can add property in stylesheet like,
MyWidget {
qproperty-margins: qmargins(1px, 1px, 1px, 1px);
}
You may need to remove any margins set by the layout. As far as I know you need to do this by code.
layout->setContentsMargins(0,0,0,0);
layout->setSpacing(0);
In qcssparser.cpp (version 5.4) they have
QVariant v;
const QVariant value = w->property(property.toLatin1());
switch (value.type()) {
case QVariant::Icon: v = decl.iconValue(); break;
case QVariant::Image: v = QImage(decl.uriValue()); break;
case QVariant::Pixmap: v = QPixmap(decl.uriValue()); break;
case QVariant::Rect: v = decl.rectValue(); break;
case QVariant::Size: v = decl.sizeValue(); break;
case QVariant::Color: v = decl.colorValue(); break;
case QVariant::Brush: v = decl.brushValue(); break;
#ifndef QT_NO_SHORTCUT
case QVariant::KeySequence: v = QKeySequence(decl.d->values.at(0).variant.toString()); break;
#endif
default: v = decl.d->values.at(0).variant; break;
}
w->setProperty(property.toLatin1(), v);
So it's no special reader for margin.

Qt OpenGL framebuffer to image

I'm drawing the framebuffer to an image, it used to work fine, however something broke and I have no idea what..
Any help would be great.
I get the error "QGLFramebufferObject: Framebuffer incomplete, missing attachment."
It seems to work intermittently.
VoxelEditor::VoxelEditor(QWidget *parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
makeCurrent();
catchFbo = new QGLFramebufferObject(PICTURE_SIZE, PICTURE_SIZE);
void VoxelEditor::renderToImage() {
saveGLState();
const int nrPics = 360 / DEGREES_BETWEEN_PICTURES;
for (int i = 0; i < nrPics; i++) {
catchFbo->bind();
glColorMask(true, true, true, true);
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_MULTISAMPLE);
glLoadIdentity();
GLfloat x = GLfloat(PICTURE_SIZE) / PICTURE_SIZE;
glFrustum(-x, +x, -1.0, +1.0, 1.0, 1000.0);
glViewport(0, 0, PICTURE_SIZE, PICTURE_SIZE);
drawScreenshot(i);
catchFbo->release();
QImage catchImage = catchFbo->toImage();
catchImage.save("object/test" + QString::number(i) + ".png");
}
glDisable(GL_MULTISAMPLE);
restoreGLState();
}
I solved this by putting the creation of the fbo in the rendertoimage call.
It seemed at creation it was valid and had the appropriate attachment, but at execution it failed..
Perhaps creating the fbo in the initializeGL call would work as well.
Have you check you buffer with isValid() method? Try to release buffer after call toImage() method.

Resources