I have the following variables:
Point of interest which is the position(x,y) in pixels of the place
to focus.
Screen width,height which are the dimensions of the window.
Zoom level which sets the zoom level of the camera.
And this is the code I have so far.
void Zoom(int pointOfInterestX,int pointOfInterstY,int screenWidth,
int screenHeight,int zoomLevel)
{
glScalef(1,1,1);
glTranslatef( (pointOfInterestX/2) - (screenWidth/2), (pointOfInterestY/2) - (screenHeight/2),0);
glScalef(zoomLevel,zoomLevel,1);
}
And I want to do zoom in/out but keep the point of interest in the middle of the screen. but so far all of my attempts have failed.
You can start the rendering of your frame like this:
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLdouble left = (0 - pointOfInterestX) / zoomLevel + pointOfInterestX;
GLdouble right = (WindowW - pointOfInterestX) / zoomLevel + pointOfInterestX;
GLdouble bottom = (WindowH - pointOfInterestY) / zoomLevel + pointOfInterestY;
GLdouble top = (0 - pointOfInterestY) / zoomLevel + pointOfInterestY;
glOrtho(left, right, bottom, top, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
Related
I'm writing a gauge widget, using QT, that is constructed from 2 separate images, one as background and the other as Needle. I reimplement paintEvent function as follow:
void myGaugeWidget::paintEvent(QPaintEvent *pe)
{
QPainter painter(this);
QPixmap bkgImage(bkgImgPath);
painter.drawPixmap(0, 0, width(), height(), bkgImage);
const double thetaDeg = 30.0;
QPixmap needle(needles[i].imgPath);
int needleWidth = 200;
int needleHeight = 200;
int anchorX = 20;
int anchorY = 30;
const int centerX = width()/2;
const int centerY = height()/2;
QTransform rm = QTransform().translate(-anchorX,- anchorY).rotate(thetaDeg).translate(centerX,centerY);
needle = needle.transformed(rm);
painter.drawPixmap(0,0, needle);
}
this code rotates my needle correctly but its position is not correct.
can anybody help me?
thanks.
This most likely would depend on your images and widget size. I have tried your code and it seems to me that QTransform().translate() is not doing anything in a QPixmap. I tried to give extreme values for translate() and removed rotate() - the image does not move.
I already have have my own implementation for a gauge. This is with painter transformation instead of the image. My images are of dimensions:
Gauge Background: 252x252 (there is some external blurring effects around the circle boundaries, making the background image larger than it seems)
Needle: 7x72 ( the image dimensions wrap around the boundaries of the needle itself)
Needle roation center (with respect to the background): 126, 126 (divide background size by 2)
The needle image points upward
For this setup, here is my paintEvent() with some explanations:
void myGaugeWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
//draw the background which is same size as the widget.
painter.drawPixmap(0, 0, bg.width(), bg.height(), bg);
//Calculate the angle of rotation.
//The gauge I am using has a cutout angle of 120 degrees at the bottom (symmetric)
float needleAngle = -120/*offset for start rotation*/ + ((value-minValue)*240/*total sweep of the gauge*//(maxValue-minValue));
painter.save();
//translate the painter to the roation center and then perform the rotation
painter.translate(126, 126);
painter.rotate(needleAngle);
//translate the rotated canvas to adjust for the height of the needle.
//If you don't do this, your needle's tip will be at the rotation center
painter.translate(0, -72);
//draw the needle and adjust for the width with the x value
painter.drawPixmap(-needle.width()/2, 0, needle.width(), needle.height(), needle);
painter.restore();
}
I am having a difficult time understanding what values I should use in the call I make to QMatrix4x4::ortho(left, right, bottom, top, near, far).
Specifically, I don't understand the documentation for the values for left,right,bottom, and top. I have a working demo that can draw the OpenGL monkey with a projection matrix. I set up the demo so that when I hit 'O' on the keyboard, it switches the projection matrix from perspective to orthographic. The perspective projection works well in that it keeps the model's aspect ratio constant (i.e. it doesn't stretch in width or height). Here's the function that gets called when either the 'O' or 'P' key gets called and updates m_proj. It's a bit of a hot mess as you can see I've tried a bunch of ideas and none of them really works the way I would like.
Thanks for any insight that would help me understand this. Other helpful details: my view eye is at z=2 facing center (0,0,0) with up being (0,1,0).
void AppGLScene::setProjectionMatrix(void)
{
m_projectionMatrix.setToIdentity();
float windowWidth = rect().width();
float windowHeight = rect().height();
float left, right, bottom, top;
float aratio = (float) windowWidth / (float) windowHeight;
qDebug() << "win wid, win hei" << windowWidth << windowHeight;
// I modify the vertical FOV in an attempt to keep the size of the
// model the same as the vertical size of the window changes.
//
float vFov = 90 * ((float)windowHeight / m_initialWinHeight);
qDebug() << "vFov" << vFov;
switch (m_proj)
{
case PROJ_PERSP:
m_projectionMatrix.perspective(vFov, qreal(windowWidth)/qreal(windowHeight), 0.5, 40);
break;
case PROJ_ORTHO:
default:
// left = rect().x();
// right = rect().x() + rect().width();
// bottom = rect().y();
// top = rect().y() + rect().height();
if (windowWidth > windowHeight)
{
left = -(3.0 - ((float)windowHeight/(float)windowWidth));
right = -left;
bottom = -3.0;
top = 3.0;
}
else
{
left = -3.0;
right = 3.0;
bottom = -(3.0 - ((float)windowWidth/(float)windowHeight));
top = -bottom;
}
qDebug() << "l r b t = " << left << right << bottom << top;
m_projectionMatrix.ortho(left, right, bottom, top, 0.5, 40);
// m_projectionMatrix.ortho(-3.0, 3.0, -3.0, 3.0, 0.5, 40);
// m_projectionMatrix.ortho(-aratio, aratio, -aratio, aratio, 0.5, 40);
break;
}
}
To avoid stretching your objects in either direction, you need to have (right - left) / (top - bottom) match the aspect ratio of the window. Which in your case you can ensure by having right be the value of top multiplied by the aspect ratio.
It looks like you want to use the range [-3.0, 3.0] for the shorter window dimension, and adjust the longer one accordingly. The above then translates into:
if (windowWidth > windowHeight)
{
top = 3.0f;
bottom = -top;
right = top * aratio;
left = -right;
}
else
{
right = 3.0f;
left = -right;
top = right / aratio;
bottom = -top;
}
Note that right / top = aratio for both cases.
Is there any way to draw an image on QPainter center aligned? I see QPainter::drawText gives us this provision but drawImage does not. I have one source rect, target rect and an image. when the source size is small the image gets drawn on the left side of the page. I want it to be printed center aligned.
The painter doesn't have a size, but the device() it paints on does. You can use QRect(painter.device()->width(), painter.device()->height()) as the rectangle where you want to center your image in.
Then you'd paint the image centered like so:
QImage source;
QPainter painter(...);
...
QRect rect(source.rect());
QRect devRect(0, 0, painter.device()->width(), painter.device()->height());
rect.moveCenter(devRect.center());
painter.drawImage(rect.topLeft(), source);
I would try to do the following (please follow the source code comments):
The sample image that should be drawn
// The image to draw - blue rectangle 100x100.
QImage img(100, 100, QImage::Format_ARGB32);
img.fill(Qt::blue);
In the paint event handler
[..]
QRect source(0, 0, 100, 100);
QRect target(0, 0, 400, 400);
// Calculate the point, where the image should be displayed.
// The center of source rect. should be in the center of target rect.
int deltaX = target.width() - source.width();
int deltaY = target.height() - source.height();
// Just apply coordinates transformation to draw where we need.
painter.translate(deltaX / 2, deltaY / 2);
painter.drawImage(source, img);
Of course you should check whether source rectangle is smaller than the target before applying this approach. I omitted that code for simplicity reasons just to demonstrate how you can center your image.
I wanted to show a more complete example with a variable image size that stays within the bounds of the area provided to add to the other great answers.
void ImageView::paintEvent(QPaintEvent*)
{
if (this->imageBuffer.empty()){ return; }
double widgetWidth = this->width();
double widgetHeight = this->height();
QRectF target(0, 0, widgetWidth, widgetHeight);
QImage tempQImage = *this->imageBuffer.at(this->imageBuffer.count()-1);
tempQImage = tempQImage.scaled(rect().size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
double imageSizeWidth = static_cast<double>(tempQImage.width());
double imageSizeHeight = static_cast<double>(tempQImage.height());
QRectF source(0.0, 0.0, imageSizeWidth, imageSizeHeight);
int deltaX = 0;
int deltaY = 0;
if(source.width() < target.width())
deltaX = target.width() - source.width();
else
deltaX = source.width() - target.width();
if(source.height() < target.height())
deltaY = target.height() - source.height();
else
deltaY = source.height() - target.height();
QPainter painter(this);
painter.translate(deltaX / 2, deltaY / 2);
painter.drawImage(source, tempQImage);
}
I'm trying to follow this article and it was easy to implement text over image and now my problem is in the above mentioned article the image watermark was placed 10 pixels from left so how do I place image similarly to top right,top middle,middle left, center,middle right and similary to bottom.
Here is how it was placed to the top right corner :
int xPosOfWm = ((phWidth - wmWidth)-10);
int yPosOfWm = 10;
grWatermark.DrawImage(
imgWatermark,
new Rectangle(
xPosOfWm, yPosOfWm,
wmWidth, wmHeight
),
0, 0,
wmWidth, wmHeight,
GraphicsUnit.Pixel,
imageAttributes
);
The problem is that you will have to calculate your image height and width first
calculate the original Image height and width
Image oImage="path";
var oheight=oImage.Height;
var oWidth=oImage.width;
Now Calculate the Image which you want to place over it
var WmImage="path";
var wWheight=WmImage.Height;
var wWidth=WmoImage.width;
top-right
var left=oWidth-wWidth-10;
var top=oheight-10;
//draw the wate mark image on thse point
oImage.DrawImage(imgWatermark,new Rectangle(left,top,wmWidth,
wmHeight),0,0,wmWidth,wmHeight,GraphicsUnit.Pixel,imageAttributes);
similarly you can calculate for other images alos.
The current code doesn't place the watermark at the top left, it places at the top right.
To place it at the top left, you use:
int xPosOfWm = 10;
int yPosOfWm = 10;
To position the watermark horisontally at the left, center and right:
int xPosOfWm = 10;
int xPosOfWm = (phWidth - wmWidth) / 2;
int xPosOfWm = (phWidth - wmWidth) - 10;
To position the watermark vertically at the top, middle and bottom:
int yPosOfWm = 10;
int yPosOfWm = (phHeight - wmHeight) / 2;
int yPosOfWm = (phHeight - wmHeight) - 10;
Just combine one horisontal with one vertical to get any combination that you want.
A long time ago I tried to find method how to stick QDialog window to screen borders for my small projects like Skype windows do it, but I failed. May be I was looking this code not in the right place, so now I'm looking the solution here, on stack! :)
So, does any one have a deal with some kind of such code, links, samples?
In my opinion, we have to reimplement QDialog moveEvent function, like below, but that code does not working:
void CDialog::moveEvent(QMoveEvent * event) {
QRect wndRect;
int leftTaskbar = 0, rightTaskbar = 0, topTaskbar = 0, bottomTaskbar = 0;
// int top = 0, left = 0, right = 0, bottom = 0;
wndRect = this->frameGeometry();
// Screen resolution
int screenWidth = QApplication::desktop()->width();
int screenHeight = QApplication::desktop()->height();
int wndWidth = wndRect.right() - wndRect.left();
int wndHeight = wndRect.bottom() - wndRect.top();
int posX = event->pos().x();
int posY = event->pos().y();
// Snap to screen border
// Left border
if (posX >= -m_nXOffset + leftTaskbar &&
posX <= leftTaskbar + m_nXOffset) {
//left = leftTaskbar;
this->move(leftTaskbar, posY);
return;
}
// Top border
if (posY >= -m_nYOffset &&
posY <= topTaskbar + m_nYOffset) {
//top = topTaskbar;
this->move(posX, topTaskbar);
return;
}
// Right border
if (posX + wndWidth <= screenWidth - rightTaskbar + m_nXOffset &&
posX + wndWidth >= screenWidth - rightTaskbar - m_nXOffset) {
//right = screenWidth - rightTaskbar - wndWidth;
this->move(screenWidth - rightTaskbar - wndWidth, posY);
return;
}
// Bottom border
if (posY + wndHeight <= screenHeight - bottomTaskbar + m_nYOffset &&
posY + wndHeight >= screenHeight - bottomTaskbar - m_nYOffset) {
//bottom = screenHeight - bottomTaskbar - wndHeight;
this->move(posX, screenHeight - bottomTaskbar - wndHeight);
return;
}
QDialog::moveEvent(event);
}
Thanks.
As you thought you can achieve this in the moveEvent function.
I guess the following code do the trick but since I have nothing to test here I will write some pseudo code:
First get the available screen area:
const QRect screen = QApplication::availableGeometry(this);
// This get the screen rect where you can drag a dialog
Then get the position of your dialog relative to the desktop (if your dialog is a child of an other widget, you need to transform coordinates from widget relative to desktop relative):
const QRect dialog = geometry();
// Do here transformation
Now test if dialog is near screen border
if( abs(dialog.left()-screen.left() < OFFSET )
move(screen.left(), dialog.top();
else if( abs(dialog.top()-screen.top() < OFFSET )
move(dialog.left(), screen.top() )
// etc. for the 2 other cases
Let me know if it works
In the pos property description from the QWidget documentation, there is the following warning about moving a window inside the move event handling method.
Warning: Calling move() or setGeometry() inside moveEvent() can
lead to infinite recursion.
That said, there is no proper way to stick the dialog window inside the screen border.
Note :
The behavior you observed in KDE comes from the Window Manager. Actually, the Window Manager is the one that arranges the application windows (like dialog boxes) to show them on the screen. The KDE Window Manager has an option to make all application windows (called client) stick to the border.