QSlider with Text at the tick marks - qt

I have some horizontal QSliders in my Qt application with 4-5 ticks marks. How can I add QLabels above the slider ticks to reflect the value at each of the ticks?
I suspect the best way to do this is to create a subclass of QSlider and override the paintEvent() method like this answer describes. But how would I add QLabels based on the position of the ticks?
For clarification:
I am looking for Text labels at each tick, ie if I have a slider that goes from 0 - 100 with 4 ticks, the ticks would have "0", "25", "50", and "100" above them, respectively. Your solution is still helpful, I believe I should be able to rework it using this->geometry to get the coordinates and dividing by # of ticks (which I believe I can calculate with (max - min/tick interval)

There is an implementation in PySide that works properly. It displays the rounded value of each tick, you can replace it with whatever text you need by updating the QPainter::drawText callback.
Long time ago, I did port that code to C++. This is the interesting part:
void MySlider::painEvent(...) {
QSlider::paintEvent(self, event)
auto round_value = std::floor(value());
auto painter = new QPainter(this);
painter->setPen(QPen(Qt::white));
auto font_metrics = QFontMetrics(this->font());
auto font_width = font_metrics.boundingRect(QString::number(round_value)).width();
auto font_height = font_metrics.boundingRect(QString::number(round_value)).height();
auto rect = w.geometry();
if (this->orientation() == Qt::Horizontal) {
auto horizontal_x_pos = rect.width() - font_width - 5;
auto horizontal_y_pos = rect.height() * 0.75;
painter->drawText(QPoint(horizontal_x_pos, horizontal_y_pos),
QString::number(round_value));
} else if (this->orientation() == Qt::Vertical) {
auto vertical_x_pos = rect.width() - font_width - 5;
auto vertical_y_pos = rect.height() * 0.75;
painter->drawText(QPoint(rect.width() / 2.0 - font_width / 2.0, rect.height() - 5),
QString::number(round_value));
} else {
return;
}
painter->drawRect(rect)
}

Related

Dynamic CButton margin

I am using this code to set the initial size of my buttons:
// Now check button sizes
auto *pDC = m_btnPreview.GetDC();
if (pDC != nullptr)
{
pDC->SelectObject(GetFont());
auto sizeActualSize = pDC->GetTextExtent(strButtonText);
sizeActualSize.cx += 10;
if (sizeActualSize.cx > rctOK.Width())
{
// We need to consistently resize both buttons
int iButtonGap = rctCancel.left - rctOK.right;
rctCancel.left = rctCancel.right - sizeActualSize.cx;
rctOK.right = rctCancel.left - iButtonGap;
rctOK.left = rctOK.right - sizeActualSize.cx;
GetDlgItem(IDOK)->MoveWindow(rctCancel);
m_btnPreview.MoveWindow(rctOK);
}
ReleaseDC(pDC);
}
It works fine. I correctly set the font so that the size returned is right. But I have added a value of 10 to pad it a bit so that the button looks nicer:
Is there a specific MFC constant for the default gap around button? Rather than fudging it with a value of 10?

How to disable linear filtering for drawImage on canvas in javafx

I'm trying to draw scaled image on canvas in javafx. Using this code:
Image image = ...;
canvas.setWidth(scale * width);
canvas.setHeight(scale * height);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.drawImage(image, 0, 0, scale * width, scale * height);
// this gives same result
// gc.scale(scale, scale);
// gc.drawImage(editableImage, 0, 0, width, height);
It works really fast but makes blurred images like this:
This is not what I'd like to see. Instead I want to get this picture:
Which can be drawn by manually setting each pixel color with such code:
PixelReader reader = image.getPixelReader();
PixelWriter writer = gc.getPixelWriter();
for (int y = 0; y < scale * height; ++y) {
for (int x = 0; x < scale * width; ++x) {
writer.setArgb(x, y, reader.getArgb(x / scale, y / scale));
}
}
But I cannot use this approach as it's too slow. It took couple of seconds to draw 1Kb image scaled 8 times. So I ask if there's any way to disable this blurry effect for drawing on canvas?
UPD 10/07/2019:
Looks like the issue is fixed! Now GraphicsContext should have property "image smoothing" controlling this behavior.
INITIAL ANSWER
I guess I've found answer to my question. As this issue says that there's no way to specify filtering options in graphics context.
Description:
When drawing an image in a GraphicsContext using the drawImage()
method to enlarge a small image to a larger canvas, the image is being
interpolated (possibly using a bilinear or bicubic algorithm). But
there are times like when rendering color maps (temperature,
zooplancton, salinity, etc.) or some geographical data (population
concentration, etc.) where we want to have no interpolation at all
(ie: use the nearest neighbor algorithm instead) in order to represent
accurate data and shapes.
In Java2D, this is possible by setting the appropriate
RenderingHints.KEY_RENDERING on the Graphics2D at hand. Currently on
JavaFX's GraphicsContext there is no such way to specify how the image
is to be interpolated.
The same applies when shrinking images too.
This could be expanded to support a better form of smoothing for the
"smooth" value that is available in both Image and ImageView and that
does not seem to work very well currently (at least on Windows).
The issue was created in 2013 but it's still untouched so unlikely it will be resolved soon.

How do I clear certain lines/paths from a QWidget but not others, then redraw separate lines/paths on top?

I'm trying to optimize line drawings on a QWidget. I basically have a grid in the background and drawings on top of the grid. Currently I'm redrawing the background and grid lines every time the paint event is called. This works fine if the grid lines are far enough apart so I don't have to draw that many lines, but if the scale gets changed, the lines must be redrawn at that new scale. Also, if the window is resized, then more of the grid is displayed, hurting the performance even more.
Here is the code for drawing the grid:
// draw grid
painter.fillRect(0,0,areaWidth, areaHeight, QColor(255,255,255));
painter.setPen(QPen(QBrush(QColor(240,240,255)), 1, Qt::SolidLine, Qt::FlatCap));
int numXLines = areaWidth/mSIToPixelScale + 1;
int numYLines = areaHeight/mSIToPixelScale + 1;
double width = areaWidth;
double height = areaHeight;
for (int x=0; x<numXLines;x++)
{
for (int y=0; y<numYLines; y++)
{
painter.drawLine(0,y*mSIToPixelScale,width, y*mSIToPixelScale);
painter.drawLine(x*mSIToPixelScale,0,x*mSIToPixelScale,height);
}
}
So when numXLines and numYLines in the above code reach higher values, the performance drops very hard, which makes sense. The grid will always have to be redrawn if the scale changes, but if the scale does not change, then only the drawing on top of the grid should change. How can I accomplish this?
The QWidget::paintEvent( QPaintEvent* aEvent ) is called by the framework not just when you want it. So if you want to remove some lines from the widget than you need draw them conditionally in your function.
For example:
if ( numXLines < 25 && numYLines < 25 )
{
// Draw only every second lines for example.
}
else
{
// Draw all lines.
}
But this is not the best way. Maybe you shall use larger steps between the grid lines if there too many of them.
I found where I went wrong, I was redrawing the y lines every x iteration. I fixed it by creating two separate for loops:
// add grid lines to a painter path
QPainterPath grid;
for (int x=0; x<numXLines;x++)
{
grid.moveTo(x*mSIToPixelScale, 0);
grid.lineTo(x*mSIToPixelScale, height);
}
for (int y=0; y<numYLines; y++)
{
grid.moveTo(0, y*mSIToPixelScale);
grid.lineTo(width,y*mSIToPixelScale);
}
painter.drawPath(grid);
painter.end();
Also, I think drawing onto a QImage first then drawing that image inside the paintEvent would make code more organized, so all your doing in the paintEvent is drawing from a high level.

hide QLabel based on text-size

I would like to write a custom QLabel subclass with some more features for responsive design. In thisexample, I want to write a QLabel which scales the text based on the useable space. This is quite easy but also has some problems because of Qt-intern stuff. (I have to scale the text to 0.9 of the useable space, otherwise resizing the window / widget gets buggy)
Now I wan't to add a way to hide the label completely when the font size is bellow a specific threshold. However, this seems to be quite a complex task.
Here is what I have sofar in the classes resizeEvent(QResizeEvent *event) function.
Right now, my function only sets the text to "" when the size would be bellow the threshold.
void CustomLabel::resizeEvent (QResizeEvent * event ) {
if(autoFontResize) {
this->setSilentText(labelText); // just the normal setText function, I overwrote it for the subclass
QFont f = this->font();
int flags = Qt::TextDontClip|Qt::TextWordWrap;
QRect fontBoundRect = this->fontMetrics().boundingRect(this->rect(), flags, this->text());
float xFactor = (float)event->size().width() / (float)fontBoundRect.width();
float yFactor = (float)event->size().height() / (float)fontBoundRect.height();
float factor = xFactor < yFactor ? xFactor : yFactor;
f.setPointSizeF(f.pointSize()*factor*0.9); //
if(minimumFontSize != 0) { // 0 = no minimum Size for the font
if(f.pointSize() < minimumFontSize) {
if(hideFontOnMinimum) { // either Hide or set to the limit size
this->setSilentText(""); //replace text
} else {
f.setPointSizeF(minimumFontSize);
}
}
}
this->setFont(f);
}
QLabel::resizeEvent(event);
}
By the way, some parts of the code are found on stackoverflow, not mine. ;)
What I would like to do is to completely hide() the label. However the label doesn't know when It can show() again since the resizeEvent doesn't seem to be called after that.
Any ideas?
Thanks!
As you've noticed, if you call hide() on the widget it fails to receive a resize event. Since you're customising the class anyway, rather than calling hide(), you could just set a class variable to note that it's hidden and overload the paintEvent function, not to draw the widget if the variable is set: -
void CustomLabel::paintEvent(QPaintEvent * event)
{
if(m_hideOnMinimum)
return;
QLabel::paintEvent(event);
}
Note that by not painting the label, it will be hidden, but the user may still be able to interact with it, so you will need to disable it or overload keyboard / mouse events too.

Image Rotate 3D in Flex, but display another image on back of this image?

i want to rotate 3D an Image called img1 in Flex. I want to rotate it around y axis 180 degree. I can do this by using 3D effect already built in Flex but i want to do a bit more different.
I want during rotating, there's another image called img2 appear on back of img1 (in default case, the image appear on the back is img1) and when rotating finish, the image will be img2.
How can i do this ?
Thank you.
If you need no perspective effect, it's quite easy to do. A rough implementation (not tested!):
// Event.ENTER_FRAME event listener
void on_enter_frame(event:Event):void
{
// m_angle is a member of the class/flex component where on_enter_frame is declared
// ANGLE_DELTA is just a constant
m_angle += ANGLE_DELTA;
// Angle clamping to the range [0, PI * 2)
m_angle %= Math.PI * 2;
if (m_angle < 0)
m_angle += Math.PI * 2;
// If we currently look at the front side...
if (m_angle < Math.PI)
{
img1.visible = true;
img2.visible = false;
img1.scaleX = Math.cos(m_angle);
}
else
{
img1.visible = false;
img2.visible = true;
// If you omit negation, the back-side image will be mirrored
img2.scaleX = -Math.cos(m_angle);
}
}
So every frame we increase the rotation angle, clamp it to the range [0, PI * 2). Then depending on the value of the rotation angle, we hide/show the pair of your images, and then perform x-scaling of the visible image.
Thank you, now i found a solution. Please check it here, it's very easy to do.
http://forums.adobe.com/thread/921258

Resources