is it possible to take a screenshot of a container like DockLayout and all of its children programmatically in BB10 (native)? I've found the Screenshot class in the docs, but it's only possible to take a display or app window screenshot...
Any tips or hints?
Thanks.
This is the basic code I use for capturing an area of the screen under program control.
Note that you can only capture a screen displayed by the program this code is in.
void ScreenCapture::capture(qreal x, qreal y, qreal width, qreal height)
{
smDebug(1) << "Capturing" << x << y << width << height;
screen_pixmap_t screen_pix;
screen_buffer_t screenshot_buf;
screen_context_t context;
char *screenshot_ptr = NULL;
int screenshot_stride = 0;
int usage, format;
int size[2];
screen_create_context(&context, 0);
screen_create_pixmap(&screen_pix, context);
usage = SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE;
screen_set_pixmap_property_iv(screen_pix, SCREEN_PROPERTY_USAGE, &usage);
format = SCREEN_FORMAT_RGBA8888;
screen_set_pixmap_property_iv(screen_pix, SCREEN_PROPERTY_FORMAT, &format);
size[0] = 0;
size[1] = 0;
screen_get_window_property_iv(Application::instance()->mainWindow()->handle(), SCREEN_PROPERTY_SIZE, size);
smDebug(1) << size[0] << 'x' << size[1];
screen_set_pixmap_property_iv(screen_pix, SCREEN_PROPERTY_BUFFER_SIZE, size);
screen_create_pixmap_buffer(screen_pix);
screen_get_pixmap_property_pv(screen_pix, SCREEN_PROPERTY_RENDER_BUFFERS,
(void**)&screenshot_buf);
screen_get_buffer_property_pv(screenshot_buf, SCREEN_PROPERTY_POINTER,
(void**)&screenshot_ptr);
screen_get_buffer_property_iv(screenshot_buf, SCREEN_PROPERTY_STRIDE,
&screenshot_stride);
screen_read_window(Application::instance()->mainWindow()->handle(), screenshot_buf, 0, NULL ,0);
QByteArray array;
int nbytes = size[0] * size[1] * 4;
write_bitmap_header(nbytes, array, size);
for (int i = 0; i < size[1]; i++)
{
array.append(screenshot_ptr + i * screenshot_stride, size[0] * 4);
}
QImage image = QImage::fromData(array, "BMP");
image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
baseImage = QImage(width,height,QImage::Format_ARGB32_Premultiplied);
baseImage.painter().drawImage(0, 0, image, x, y, width, height);
if (mWaterMarkImage != NULL)
baseImage.updateToView(mWaterMarkImage);
soundplayer_play_sound("event_camera_shutter");
screen_destroy_pixmap(screen_pix);
smDebug(1) << "capture done.";
}
Related
I wrote a program in Qt, which visualizes a processed pointcloud (3D-points) by using Q3DScatter.
Now I want to add calculated keypoints with a different color.
Is that possible?
Does anyboy have some experiences with that?
Below you can see the part of code, where the point cloud is added to the data array.
QScatterDataArray * dataArray = new QScatterDataArray;
dataArray->resize(vector_seg_x->size());
QScatterDataItem * ptrToDataArray = &dataArray->first();
for(int i = 0; i < vector_seg_x->size();i++){
ptrToDataArray->setPosition(QVector3D(
(double)(iter_seg_x[i]),
(double)(iter_seg_y[i]),
(double)(iter_seg_z[i])));
ptrToDataArray++;
}
m_graph_seg->seriesList().at(0)->dataProxy()->resetArray(dataArray);
You can only set a color/gradient for a whole point list:
Q3DScatter scatter;
scatter.setFlags(scatter.flags() ^ Qt::FramelessWindowHint);
scatter.addSeries(new QScatter3DSeries);
scatter.addSeries(new QScatter3DSeries);
{
QScatterDataArray *data = new QScatterDataArray;
*data << QVector3D(0.5f, 0.5f, 0.5f) << QVector3D(-0.3f, -0.5f, -0.4f) << QVector3D(0.0f, -0.3f, 0.2f);
scatter.seriesList().at(0)->dataProxy()->resetArray(data);
QLinearGradient linearGrad(QPointF(100, 100), QPointF(200, 200));
linearGrad.setColorAt(0, Qt::blue);
linearGrad.setColorAt(1, Qt::red);
scatter.seriesList().at(0)->setBaseGradient(linearGrad);
scatter.seriesList().at(0)->setColorStyle(Q3DTheme::ColorStyle::ColorStyleObjectGradient);
}
{
QScatterDataArray *data = new QScatterDataArray;
*data << QVector3D(0.f, 0.f, 0.f);
scatter.seriesList().at(1)->dataProxy()->resetArray(data);
scatter.seriesList().at(1)->setBaseColor(Qt::green);
}
scatter.show();
I'm doing some processing on an Amazon Linux EC2 AWS server. When something goes wrong I need to send an email with a bar graph. I want to generate a bar graph myself, and I'm doing so by using QPainter and QImage. I'm developing an example application to simply generate the bar graph, right now. So what I do is I develop on my laptop and when I have something that works upload the code to the EC2 server and compile it there. Then run the resulting application in the server.
However, here is the hiccup. When I tried to draw text using QPainter, the program in my laptop crashes. This is because I use a QCoreApplication instead of QApplication in my main.cpp.
So I change to QApplication which fixes the problem. I upload the code to the server and compile it. Then when I try to run it, I get:
qt.qpa.screen: QXcbConnection: Could not connect to display
Could not connect to any X display.
This is, of course, because the server is headless.
My question is: is there something I can install in the server that would allow me to fully use QPainter's capabilities?
Edit:
My main.cpp
#include <QApplication>
#include "../../CommonClasses/DataAnalysis/BarGrapher/bargrapher.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Setup
qreal W = 1024;
qreal H = 768;
// A fictional data set;
QList<qreal> ds1;
ds1 << 50 << 500 << 368 << 198;
QStringList descs;
descs << "Item 1";
descs << "A somewhat long and complex description";
descs << "Item 2";
descs << "Item Another";
// The actual bar graph
BarGrapher bg;
bg.configureGraphText(W,H,descs,"Some sort of label","The title of this damn thing");
BarGrapher::DataSet ds;
ds << ds1;
QImage result = bg.generateGraph(ds);
qWarning() << "ERROR" << bg.getError();
result.save("mytest.png");
qWarning() << "DONE";
return a.exec();
}
My bargrapher.cpp
#include "bargrapher.h"
const qreal BarGrapher::PROYECTION_FACTOR = 0.707106;
const qreal BarGrapher::LEFT_MARGIN = 0.1;
const qreal BarGrapher::RIGHT_MARGIN = 0.04;
const qreal BarGrapher::TOP_MARGIN = 0.08;
BarGrapher::BarGrapher()
{
}
void BarGrapher::configureGraphText(qreal width, qreal height, const QStringList &xItems, const QString &ylabel, const QString &title){
graphTitle = title;
W = width;
H = height;
itemDescriptions = xItems;
yAxisText = ylabel;
}
QImage BarGrapher::generateGraph(const DataSet &dataSet){
QImage graph(W,H,QImage::Format_RGB888);
error = "";
// Making sure that the data is coherent.
if (dataSet.isEmpty()){
error = "No data was presented";
return graph;
}
if (dataSet.size() > 2){
error = "Cannot have more than two data sets, passed " + QString::number(dataSet.size());
return graph;
}
qint32 numItems = dataSet.first().size();
for (qint32 i = 0; i < dataSet.size(); i++){
if (numItems != dataSet.at(i).size()){
error = "All data sets must be the same size: " + QString::number(numItems) + " however data set " + QString::number(i) + " has " + QString::number(dataSet.at(i).size());
return graph;
}
}
if (!itemDescriptions.isEmpty()){
if (numItems != itemDescriptions.size()){
error = "The number of item descriptios (" + QString::number(itemDescriptions.size()) + ") must coincide with the numer of points in the data set (" + QString::number(numItems) + ")";
return graph;
}
}
else{
for (qint32 i = 0; i < numItems; i++){
itemDescriptions << QString::number(i);
}
}
// The font to be used
QFont textFont("Mono");
textFont.setPixelSize(10);
//QFont titleFont("Mono",16,QFont::Bold);
// Calculating margins to get the effective graph area.
qreal XAxisLength = (1.0 - LEFT_MARGIN - RIGHT_MARGIN)*W;
qreal DW = XAxisLength/(qreal)(numItems);
// Calculating the effective graph area due to the text items.
qreal Th = 0.1*H;
qreal GH = (1.0-TOP_MARGIN)*H - Th;
qreal xOffset = LEFT_MARGIN*W;
qreal yOffset = TOP_MARGIN*H;
// --------------- Commence drawing -----------------------
QPainter painter(&graph);
// The background
painter.setBrush(QBrush(QColor(226,226,226)));
painter.drawRect(0,0,W,H);
// Drawing the axis;
QPen pen;
pen.setColor(QColor(0,0,0));
pen.setWidth(2);
painter.setPen(pen);
painter.setBrush(QBrush(Qt::black));
painter.drawLine(xOffset,yOffset+GH,xOffset + XAxisLength,yOffset+GH);
painter.drawLine(xOffset,yOffset,xOffset,yOffset+GH);
// Drawing the X Axis text
painter.setFont(textFont);
for (qint32 i = 0; i < numItems; i++){
QRectF rectF(xOffset + i*DW,yOffset+GH,DW,Th);
painter.drawText(rectF,itemDescriptions.at(i));
}
return graph;
}
I'm using Qt charts module to draw a Nested Donuts chart just like the example in Qt Charts.
And I want every components (QPieSlice) response to the mouse event, the hovered() signals work well, however, the clicked() signals only work for the QpieSlices in the last added QPieSerie. It seems other QpieSlices do not emit the signals, since if I explicitly call the clicked() function, the slot response correctly.
This piece of code shows the issue
Widget::Widget(QWidget *parent): QWidget(parent){
QChartView *chartView = new QChartView;
QChart *chart = chartView->chart();
for (int i = 0; i < donutCount; i++) {
QPieSeries *donut = new QPieSeries;
donut->setHoleSize(minSize + i * (maxSize - minSize) / donutCount);
donut->setPieSize(minSize + (i + 1) * (maxSize - minSize) / donutCount);
int sliceCount = 3 + qrand() % 3;
for (int j = 0; j < sliceCount; j++) {
qreal value = 100 + qrand() % 100;
QPieSlice *slice = new QPieSlice(QString("%1").arg(value), value);
slice->setLabelVisible(true);
slice->setLabelColor(Qt::white);
slice->setLabelPosition(QPieSlice::LabelInsideTangential);
connect(slice, SIGNAL(hovered(bool)), this, SLOT(explodeSlice(bool)));
connect(slice, SIGNAL(clicked()), this, SLOT(selected()));
donut->append(slice);
}
m_donuts.append(donut);
chartView->chart()->addSeries(donut);
}
void Widget::selected()
{
QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
cout << slice->label().toStdString() << endl;
}
What am I doing wrong? Can someone help me?
In my Qt application, I want to create a preview page with the content that contains the Header, Footer title and a TableView.
This is the code I used:
void MainWindow::print(QPrinter *printer)
{
int xscale = 50;
int yscale = 30;
QPoint top_left = QPoint(xscale, yscale);
QPoint top_right = QPoint(xscale + 552, yscale + 20);
QPoint bottom_left = QPoint(xscale, yscale + 1020);
QPoint bottom_right = QPoint(xscale + 492, yscale + 1020);
QPainter painter(printer);
painter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing |
QPainter::SmoothPixmapTransform, true);
// Header
painter.setFont(QFont("Arial", 10));
painter.drawImage(top_left, QImage(":/images/images/logo.png"));
painter.drawText(top_right, "Header");
// Print the Table
QString strStream;
QTextStream out(&strStream);
out << "<html>\n"
"<head>\n"
"<meta content=\"text/html; charset=utf-8\">\n"
"<title>Demo MyTableView</title>\n"
"<style tyle=\"text/css\">th{font-size: 14pt}\n td{font-size: 12pt}\n table td + td + td + td{font-weight:bold}</style>"
"</head>\n"
"<body bgcolor=#ffffff link=#5000A0>\n"
"<table cellspacing=\"0\" cellpadding=\"2\" border=\"1\" width=\"100%\">\n";
// Print the headers
out << "<thead><tr bgcolor=\"#ffffff\">";
for (int column = 0; column < columnCount; column++)
if (!myTableView->isColumnHidden(column))
out << QString("<th>%1</th>").arg(myTableView->model()->headerData(column, Qt::Horizontal).toString());
out << "</tr></thead>\n";
// Print the data
for (int row = 0; row < rowCount; row++) {
out << "<tr>";
for (int column = 0; column < columnCount; column++) {
if (!myTableView->isColumnHidden(column)) {
QString data = myTableView->model()->data(myTableView->model()->index(row, column)).toString().simplified();
out << QString("<td bkcolor=0 align=center>%1</td>").arg((!data.isEmpty()) ? data : QString(" "));
}
}
out << "</tr>\n";
}
out << "</table>\n"
"</body>\n"
"</html>\n";
QTextDocument *document = new QTextDocument();
document->setHtml(strStream);
document->print(printer); // I got the error messages at here
delete document;
// Footer
painter.setFont(QFont("Arial", 10));
painter.drawText(bottom_left, "Copyright 2013");
// Get current date and time
QDateTime dateTime = QDateTime::currentDateTime();
QString dateTimeString = dateTime.toString();
painter.drawText(bottom_right, dateTimeString);
}
When I run the application, I only see Header and Footer title in the preview page, the TableView is not shown. Then I used qDebug() to check and I got the error messages
QPrinter::setDocName: Cannot be changed while printer is active
QPainter::begin: A paint device can only be painted by one painter at a time.
at the line
document->print(printer);
How can I solve this issue to print the data normally with the Header, Footer title and TableView?
Thanks for your help!
Well, if Qt complains about using multiple painters at a time, make it use only one:) In other words, just split the code in your MainWindow::print() function into the smaller routines for each part of your document:
void MainWindow::drawHeader(QPrinter *printer)
{
QPainter painter(printer);
// .. Draw the header
[..]
}
void MainWindow::drawFooter(QPrinter *printer)
{
QPainter painter(printer);
painter.setFont(QFont("Arial", 10));
painter.drawText(bottom_left, "Copyright 2013");
[..]
}
void MainWindow::drawTable(QPrinter *printer)
{
QTextDocument document;
document.print(printer);
[..]
}
And finally:
void MainWindow::print(QPrinter *printer)
{
// Init something.
drawHeader(printer);
drawTable(printer);
drawFooter(printer);
[..]
}
Based on a grayscale image and an ordered closed polygon (may be concave), I want to get all grayscale values that lie inside a region of interest polygon (as likewise described in SciPy Create 2D Polygon Mask). What is the most performant realisation of that in Qt 4.8? Endpoint should be some kind of QList<double>. Thanks for your advices.
In addition, is it possible to compute a floating point mask (e.g. 0 for outside the polygon, 0.3 for 30% of the pixel area is within the polygon, 1 for completely inside the polygon)? However, that's just an extra, endpoint would be QPair<double, double> (percentage, value) then. Thanks.
First you need to scanline convert the polygon into horizontal lines -- this is done by getScanLines(). The scanlines are used to sample the image to get all the values within the endpoints of each line, using scanlineScanner().
Below is a complete, standalone and compileable example I had laying around to show that the scanline algorithm is well behaved. It could be tweaked to calculate the fixed point mask as well. So far a point is included if the scanline covers more than half of it in the horizontal extent (due to round()s in scanlineScanner).
Upon startup, resize the window and click around to define consecutive points in the polygon. The polygon you see is rendered solely using the scanlines. For comparison, you can enable the outline of the polygon.
I'm sure the scanline converter could be further optimized. I'm not doing any image sampling, but the scanlineScanner is there to show that it's a trivial thing to do.
// https://github.com/KubaO/stackoverflown/tree/master/questions/scanline-converter-11037252
#define QT_DISABLE_DEPRECATED_BEFORE 5
#include <QtGui>
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <algorithm>
typedef QVector<QPointF> PointVector;
typedef QVector<QLineF> LineVector;
// A list of vertex indices in the polygon, sorted ascending in their y coordinate
static QVector<int> sortedVertices(const QPolygonF & poly)
{
Q_ASSERT(! poly.isEmpty());
QVector<int> vertices;
vertices.reserve(poly.size());
for (int i = 0; i < poly.size(); ++i) { vertices << i; }
std::sort(vertices.begin(), vertices.end(), [&](int i, int j){
return poly[i].y() < poly[j].y();
});
return vertices;
}
// Returns point of intersection of infinite lines ref and target
static inline QPointF intersect(const QLineF & ref, const QLineF & target)
{
QPointF p;
target.intersect(ref, &p);
return p;
}
// Allows accessing polygon vertices using an indirect index into a vector of indices.
class VertexAccessor {
const QPolygonF & p;
const QVector<int> & i;
public:
VertexAccessor(const QPolygonF & poly, const QVector<int> & indices) :
p(poly), i(indices) {}
inline QPointF operator[](int ii) const {
return p[i[ii]];
}
inline QPointF prev(int ii) const {
int index = i[ii] - 1;
if (index < 0) index += p.size();
return p[index];
}
inline QPointF next(int ii) const {
int index = i[ii] + 1;
if (index >= p.size()) index -= p.size();
return p[index];
}
};
// Returns a horizontal line scanline rendering of an unconstrained polygon.
// The lines are generated on an integer grid, but this could be modified for any other grid.
static LineVector getScanlines(const QPolygonF & poly)
{
LineVector lines;
if (poly.isEmpty()) return lines;
const QVector<int> indices = sortedVertices(poly);
VertexAccessor vertex{poly, indices};
const QRectF bound = poly.boundingRect();
const auto l = bound.left();
const auto r = bound.right();
int ii = 0;
int yi = qFloor(vertex[0].y());
QList<int> active;
PointVector points;
forever {
const qreal y = yi;
const QLineF sweeper{l, y, r, y};
// Remove vertex from the active list if both lines extending from it are above sweeper
for (int i = 0; i < active.size(); ) {
const int ii = active.at(i);
// Remove vertex
if (vertex.prev(ii).y() < y && vertex.next(ii).y() < y) {
active.removeAt(i);
} else {
++ i;
}
}
// Add new vertices to the active list
while (ii < poly.count() && vertex[ii].y() < y) {
active << ii++;
}
if (ii >= poly.count() && active.isEmpty()) break;
// Generate sorted intersection points
points.clear();
for (auto ii : active) {
const auto a = vertex[ii];
auto b = vertex.prev(ii);
if (b.y() >= y)
points << intersect(sweeper, QLineF{a, b});
b = vertex.next(ii);
if (b.y() >= y)
points << intersect(sweeper, QLineF{a, b});
}
std::sort(points.begin(), points.end(), [](const QPointF & p1, const QPointF & p2){
return p1.x() < p2.x();
});
// Generate horizontal fill segments
for (int i = 0; i < points.size() - 1; i += 2) {
lines << QLineF{points.at(i).x(), y, points.at(i+1).x(), y};
}
yi++;
};
return lines;
}
QVector<int> scanlineScanner(const QImage & image, const LineVector & tess)
{
QVector<int> values;
for (auto & line : tess) {
for (int x = round(line.x1()); x <= round(line.x2()); ++ x) {
values << qGray(image.pixel(x, line.y1()));
}
}
return values;
}
class Ui : public QWidget
{
Q_OBJECT
QPointF lastPoint;
QPolygonF polygon;
LineVector scanlines;
QGridLayout layout{this};
QPushButton reset{"Reset"};
QCheckBox outline{"Outline"};
public:
Ui() {
setMinimumSize(200, 200);
layout.addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding}, 0, 0, 1, 3);
layout.addWidget(&reset, 1, 0);
layout.addWidget(&outline, 1, 1);
layout.addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding}, 1, 2);
reset.setObjectName("reset");
outline.setObjectName("outline");
QMetaObject::connectSlotsByName(this);
}
protected:
Q_SLOT void on_reset_clicked() {
polygon.clear();
scanlines.clear();
lastPoint = QPointF{};
update();
}
Q_SLOT void on_outline_stateChanged() {
update();
}
void paintEvent(QPaintEvent *) override {
QPainter p{this};
if (false) p.setRenderHint(QPainter::Antialiasing);
p.setPen("cadetblue");
if (!polygon.isEmpty() && scanlines.isEmpty()) {
scanlines = getScanlines(polygon);
qDebug() << "new scanlines";
}
p.drawLines(scanlines);
if (outline.isChecked()) {
p.setPen("orangered");
p.setBrush(Qt::NoBrush);
p.drawPolygon(polygon);
}
if (!lastPoint.isNull()) {
p.setPen("navy");
p.drawEllipse(lastPoint, 3, 3);
}
}
void mousePressEvent(QMouseEvent * ev) override {
lastPoint = ev->posF();
polygon << ev->posF();
scanlines.clear();
update();
}
};
int main(int argc, char** argv)
{
QApplication app{argc, argv};
Ui ui;
ui.show();
return app.exec();
}
#include "main.moc"