My QGraphicsPixmapItem has to report a correct size - its initial size should match the original image size.
I notice something odd if I make the item selectable: the size reported is one pixel too large.
Is this to be expected ?
Will this behavior be consistent for all QGraphicsPixmapItems that are set selectable ?
(And can I therefore override the boundingRect() to subtract 1 from the size reported by the QGraphicsPixmapItem::boundingRect() each time ?)
Simple check, with any image:
QGraphicsPixmapItem p;
p.setFlags(QGraphicsItem::ItemIsSelectable);
QString fileName = QFileDialog::getOpenFileName(0, QObject::tr("Open Image File"),
QString(), QObject::tr(
"Png files (*.png);;Jpeg files (*.jpg *.jpeg);;Bitmap files (*.bmp)"));
QPixmap pixmap(fileName);
qDebug("%d %d", pixmap.size().width(), pixmap.size().height());
p.setPixmap(pixmap);
qDebug("%f %f", p.boundingRect().width(), p.boundingRect().height());
This is expected behavior. If you look at the source code, you will see that it adds half a pixel to each direction when the ItemIsSelectable flag has been set:
if (d->flags & ItemIsSelectable) {
qreal pw = 1.0;
return QRectF(d->offset, d->pixmap.size()).adjusted(-pw/2, -pw/2, pw/2, pw/2);
}
Related
recently i start to learn Qt and now i'm working on GCS project that it must have a map with some tiled imges and and some graphics item like Plan,the path and also on over off all some gauge.
so we have 3 kind of item:
Tiled map in the background so that its change by scrolling .
in the middle there is a picture of airplane that move by gps changes and also its way .
on the all on off these items there 3 or 4 gauge like speed meter, horizontal gauge and altimeter gauge there are must be solid in somewhere of graphicsview and not change when scrolling down/up or left right
The question is what is the best way to implement this ?
here is first look of my project:
in first look gauge are not over map but i want to be ! i want to have bigger map screen with gauges include it !
And here is map updater code :
void mainMap::update()
{
m_scene->clear();
QString TilePathTemp;
QImage *imageTemp = new QImage();
int X_Start=visibleRect().topLeft().x()/256;
int X_Num=qCeil((float)visibleRect().bottomRight().x()/256.0f-(float)visibleRect().topLeft().x()/256.0f);
int Y_Start=visibleRect().topLeft().y()/256;
int Y_Num=qCeil((float)visibleRect().bottomRight().y()/256.0f-(float)visibleRect().topLeft().y()/256.0f);
LastCenterPoint->setX(visibleRect().center().x());
LastCenterPoint->setY(visibleRect().center().y());
X_Start=(X_Start-X_MAP_MARGIN)>0?(X_Start-X_MAP_MARGIN):0;
Y_Start=(Y_Start-Y_MAP_MARGIN)>0?(Y_Start-Y_MAP_MARGIN):0;
X_Num+=X_MAP_MARGIN;
Y_Num+=Y_MAP_MARGIN;
qDebug()<<"XS:"<<X_Start<<" Num:"<<X_Num;
qDebug()<<"YS:"<<Y_Start<<" Num:"<<Y_Num;
for(int x=X_Start;x<=X_Start+X_Num;x++){
for(int y=Y_Start;y<=Y_Start+Y_Num;y++){
if(Setting->value("MapType",gis::Hybrid).toInt()==gis::Hybrid) TilePathTemp=Setting->value("MapPath","/Users/M410/Documents/Map").toString()+"/Hybrid/gh_"+QString::number(x)+"_"+QString::number(y)+"_"+QString::number(ZoomLevel)+".jpeg" ;
else if(Setting->value("MapType",gis::Sattelite).toInt()==gis::Sattelite) TilePathTemp=Setting->value("MapPath","/Users/M410/Documents/Map").toString()+"/Sattelite/gs_"+QString::number(x)+"_"+QString::number(y)+"_"+QString::number(ZoomLevel)+".jpeg" ;
else if(Setting->value("MapType",gis::Street).toInt()==gis::Street) TilePathTemp=Setting->value("MapPath","/Users/M410/Documents/Map").toString()+"/Street/gm_"+QString::number(x)+"_"+QString::number(y)+"_"+QString::number(ZoomLevel)+".jpeg" ;
QFileInfo check_file(TilePathTemp);
// check if file exists and if yes: Is it really a file and no directory?
if (check_file.exists() && check_file.isFile()) {
// qDebug()<<"Exist!";
imageTemp->load(TilePathTemp);
QPixmap srcImage = QPixmap::fromImage(*imageTemp);
//QPixmap srcImage("qrc:/Map/File1.jpeg");
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(srcImage);
item->setPos(QPointF(x*256, y*256));
m_scene->addItem(item);
// centerOn( width() / 2.0f , height() / 2.0f );
} else {
qDebug()<<"NOT Exist!";
}
}
}
Really, you should consider using QML. The advantage of using QML instead of QGraphicsView is you can iterate a lot faster than if you were working directly in C++. The primary downside is generally increased memory usage and incompatibility with QWidgets.
So if you need unique graphics, and very little "standard widget" stuff, you should use QML first and then QGraphicsView ONLY IF requirements dictate it.
Specific to your project though, Qt has a Map type which could be useful: https://doc.qt.io/qt-5/qml-qtlocation-map.html
I have created a simple application and I need export from pixmap to the 16-bit bmp image. I have several pixmap items so I have the for loop like this where I first create QImage and convert it to Format_RGB16:
for(QList<image_handler * >::iterator it=imageItems->begin(); it!=imageItems->end(); it++)
{
...
// image_handler inherits QPixmap
QFile export_image(path+"/img_"+code+".bmp");
QImage export_img = (*it)->toImage().convertToFormat(QImage::Format_RGB16);
export_img.save(&export_image, "BMP");
...
}
where image_handler is my custom QPixmap. Images are exported at path given, with correct filename. However when I look at properties of file (in windows) I can see that image depth is 24-bit. Unfortunately I need them to be 16-bit.
What I am doing wrong here? Or is this a bug in Qt? Then how can I export 16-bit bmps from pixmap?
Turns out, that Qt forcibly converts image, before saving it to bmp.
qt-src/src/gui/image/qbmphandler.cpp:777:
bool QBmpHandler::write(const QImage &img)
{
QImage image;
switch (img.format()) {
case QImage::Format_ARGB8565_Premultiplied:
case QImage::Format_ARGB8555_Premultiplied:
case QImage::Format_ARGB6666_Premultiplied:
case QImage::Format_ARGB4444_Premultiplied:
image = img.convertToFormat(QImage::Format_ARGB32);
break;
case QImage::Format_RGB16:
case QImage::Format_RGB888:
case QImage::Format_RGB666:
case QImage::Format_RGB555:
case QImage::Format_RGB444:
image = img.convertToFormat(QImage::Format_RGB32);
break;
default:
image = img;
}
...
So, if you need to save bmp 16bit, you'll have to do it manually, filling header and using QImage::bits() and QImage::byteCount().
I started with this
void draw_text (QPainter & p, const QString & text, QRectF target)
{
float scale = calculate_font_scale (p, text, target); // about 0.0005
QFont f = p .font ();
float old_size = f .pointSizeF ();
f .setPointSizeF (old_size * scale);
p .setFont (f);
// this prints the new font size correctly
qWarning ("old: %f, new: %f", old_size, p .font () .pointSizeF ());
// but that doesn't seem to affect this at all
p .drawText (position, text);
}
The QPainter's font has size has been correctly updated, as the qWarning line indicates, but the text draws much, much to big. I think this is because the QPainter coordinate system has been zoomed-in quite a lot and it seems setPointSizeF only works with sizes of at least 1. By eye it seems that the font is one "unit" high so I'll buy that explanation, although it's stupid.
I experimented with using setPixelSize instead, and although p.fontMetrics().boundingRect(text) yields a sane-looking answer, it is given in pixel units. One requirement for the above-function is that the bounding rect of the text is horizontally and vertically centred with respect to the target argument, which is in coordinates of a vastly different scale, so the arithmetic is no longer valid and the text is drawn miles off-screen.
I want to be able to transform the coordinate system arbitrarily and if, at the point, one "unit" is a thousand pixels high and I'm drawing text in a 0.03x0.03 unit box then I want the font to be 30 pixels high, obviously, but I need all my geometry to be calculated in general units all the time, and I need fontMetrics::boundingRect to be in these same general units.
Is there any way out of this or do I have to dick around with pixel calculations to appease the font API?
You simply have to undo whatever "crazy" scaling there was on the painter.
// Save the state
p.save();
// Translate the center of `target` to 0,0.
p.translate(-target.center());
// Scale so that the target has a "reasonable" size
qreal dim = 256.0;
qreal sf = dim/qMin(target.height(), target.width());
p.scale(sf, sf);
// Draw your text
p.setPointSize(48);
p.drawText(QRectF(dim, dim), Qt::AlignCenter | Qt::WordWrap, text);
// Restore the state
p.restore();
Hy all, in Qt i have:
FILE *pInFile = fopen(strFileName.toLatin1().constData(), "r");
QFileInfo fi(strFileName);
qint64 fileSize = fi.size();
//GO TO THE MIDDLE
//WHAT IS THE POSITION OF THE MIDDLE (INTEGER)
Instead of reading trough the whole file (fgets) in a loop, i want to know the offset of the middle of the file. And based on that offset i want to get that position.
Basically;
get offset from a file based on my given bytes (example: fileSize/2)
based on that offset what is the position (row index)
Is it possible to have something like this for determining position?
int centerPos = ftell(Offset middle, pInFile);
I don't think i'm on the right path here, can u give some advice?
Thx
ps.
And would be nice to position on the the beginning of the position
Use QFile instead of a raw file pointer. It has a seek method.
http://doc.qt.nokia.com/latest/qfile.html#seek
You can determine the middle of the file using
QFile myfile("filename");
int middle = myfile.size() /2;
...then, as mentioned, start reading at that position after calling
myfile.seek(middle).
Open the file in append mode.
do ftell() to know the end position of file.
Calculate middle of file by halving step 2, and store it in a variable named middle.
Then define the following function
void middle_fseek(FP* FilePointer, int offset)
{
fseek(FilePointer, middle+ offset, SEEK_SET);
}
I am using OpenMap and have to load very large dimensioned images.
I tried to load these images as big raster which fails with an OufOfMemoryException. In debug mode the layer constructor tells me that the image dimensions are too large.
In an OpenMap mailing list I found the MyJAIPlugin, which allows me to load and display GeoTiff files.
How can I show a 300mb GeoTiff in OpenMap?
I had a nearly same situation by loading hd maps with at least 690mb filesize.
I also used the JAIPlugIn from the mailing list and internaly they use the OMScalingRaster witch works with a BufferedImage. These limits your image size and causes the debug message.
I've solved it by modifieing the OMScalingRaster. I've changed the BufferedImage to a TiledImage to handle large images and fixed the upcoming errors. Here it's important you change the scaleTo(Projection thisProj)-method, to scale with JAI.
Now i can load the file and it's rendered on the map. But if you are zooming out too much, it will throw a OutOfMemoryException because in my modification i make a subimage of the part of the image that will be visible and give it as BufferedImage to the OMRaster.
Here is the mod. at the end of the scaleTo-method:
// Now we can grab the bit we want out of the source
// and
// scale it to fit the intersection.
// Calc width adjustment
float widthAdj = (float) ((double) iRect.width
/ (double) clipRect.width);
// Calc height adjustment
float heightAdj = (float) ((double) iRect.height
/ (double) clipRect.height);
// Create the transform
// JAI-Version
ParameterBlock pb = new ParameterBlock();
pb.addSource(sourceImage.getSubImage(clipRect.x,
clipRect.y,
clipRect.width,
clipRect.height).getAsBufferedImage());
pb.add(widthAdj); // The xScale
pb.add(heightAdj); // The yScale
pb.add(0.0F); // The x translation
pb.add(0.0F); // The y translation
RenderedOp newImage = JAI.create("scale",pb, null);
bitmap = newImage.getAsBufferedImage();
point1.setLocation(iRect.x, iRect.y);
// setVisible(currentVisibility);
}
} else {
bitmap = null;
}
}
For the other errors by replacing BufferedImage with TiledImage use the equivalent TiledImage-methods. But to save memory you should use the TiledImage-constructor with the sharedDataBuffer flag = true.
For Exsample this mod. can handle maps (compressed 690mb) with a scaling of 1:50000 and i can zoom out to 1:600000 before the layer says it run out of memory.