Scaling only works one way with QTransform? - qt

I have a problem with scaling an image in QT. I have a GraphicsView that has an image displayed and I have put in a slider to be able to allow the user to zoom in and out. For this, I've been using the scale function of the QTransform class to achieve this. It generally works if I zoom in, ie, if value gets bigger and bigger. However, when I go the opposite direction (making value smaller and smaller), the entire image disappears. Zooming back in doesn't fix the problem either; the entire image is no longer present no matter how much I zoom in or out. So the following are snippets of my code.
void MainWindow::on_ZoomSlider_valueChanged(int value)
{
QTransform transformer;
transformer.scale(scaler(value),scaler(value));
ui->graphicsView->setTransform(transformer);
}
qreal MainWindow::scaler(int value)
{
if(value < 0)
return 1/(-(value-1));
else if(value >0)
return value+1;
else
return value+1;
}
I'd be happy to offer any more clarification on my problem if it'd help you help me figure out what's wrong.

The 1/(-(value-1)) expression evaluates as an INTEGER, which is always zero in your case. Try using 1.0/(-(value-1)).
Also, I'm not really familiar with sliders, but I don't understand why could the value be negative. The valueChanged signal comes with the actual value, not the delta.

Related

Is there a way to reset the rotation of a linear gradient?

I am using a linear gradient with d3 to make a bunch of lines go from transparent to black.
The issue is that they're all rotated at random angles and I've so far used this:
.style('stroke', function rotateGrad (d)
{
linearGradient.attr('gradientTransform', `rotate(${d.angle.toFixed()})`)
return 'url(#linear-gradient)'
})
.attr('rerotate', function rotateGrad (d)
{
console.log(d.angle.toFixed());
linearGradient.attr('gradientTransform', `rotate(${-(d.angle.toFixed())})`)
})
Which in itself is probably a horrible way to go about.
It works for most, but I think the gradient which gets reused and continuously rotated back and forth goes a bit off and doesn't start at angle 0 again. So for some lines that are rendered later I can tell that the rotation is off.
Is there a way to reset the rotation back to 0?
I mainly followed Mike's example here.
Many thanks in advance!

How to get the length of the left/right offsets in a QSlider?

I'm trying to make a subclass of QSlider that allows the user to insert "bookmarks" so they can remember significant locations on the slider. I've run into a problem painting the tabs on the slider - using QStyle.sliderPositionFromValue, I get a value but it is slightly inaccurate. If I'm on the left side of the slider, the tab is painted too far left, and on the right side it is painted too far right. I believe this is because QSlider.width() returns the width of the whole object, including the small offsets at the left and right. So width() might return 630 pixels, when the length of the slider itself is really 615.
This is the code I'm using to get the pixel position and draw a line across the slider.
pos = QStyle.sliderPositionFromValue(self.minimum(),self.maximum(),sliderIndex,self.width())
painter.drawLine(pos,0,pos,self.height())
I've been looking at the QT Source here starting on line 2699 and it seems like I need to be using the PixelMetric class from QStyle. I've tried using:
self.style().pixelMetric(QStyle.PM_SliderSpaceAvailable)
But that returns 0, which is clearly not the value I need.
I'd appreciate any advice.
Edit: As suggested in the comments, I changed the call to:
self.style().pixelMetric(QStyle.PM_SliderSpaceAvailable, QStyleOption(1,QStyleOption.SO_Slider),self)
This however, returns -14, which also doesn't match for the value of the offsets (I tried using self.width()-14 but the offset remains.

qscrollbar (not a qscrollarea) setValue not working

I am working on a qwtPlot and have implemented custom scrollbars to illustrate the position of things on the plot when zooming in (in regards to the whole thing - so basically the percentage).
Now, everything works fine, apart from the moment, when I do any zooming or panning right at the beginning (or simply when I see the whole plot and then I want to zoom in).
This is a slot I am using to refresh the appearance of the scrollbar:
void ScrollHorizontal::refreshAfterChanges() {
setValue(myPlot->getLowerBound(QwtPlot::xBottom));
setPageStep(myPlot->get_X_delta());
setSingleStep(myPlot->get_X_delta()/10);
setMaximum(myPlot->dataFromSources->lastTimeValMicro()-pageStep());
update();
printV("HorizontalScroll::setValue",myPlot->getLowerBound(QwtPlot::xBottom));
printV("value", value());
printV("pageStep", pageStep());
in the constructor I set the maximum to 0 (just in case, but it doesn't change anything)
The last 3 lines print out some values useful for debugging. What I found out thanks to them is that the slot is executed correctly, but the setValue(int) function doesn't work as I would expect it to:
//printed values right after starting the program
HorizontalScroll::setValue=0
value=0
pageStep=7320
//printed values after using the zoomer once
HorizontalScroll::setValue=956.316
value=0
pageStep=2225
Then, when I move the plot a tiny bit, e.g. zoom it 1.1 times, the setValue works properly and value() return the same thing I set. But if I go to the 100% view (the starting point) I get the same problems again.
Just to illustrate it, here are some screenshots:
http://tinypic.com/view.php?pic=2znph7s&s=8#.UvNkDoZhauY
(100% view, right before zooming in)
http://tinypic.com/view.php?pic=ke7nn9&s=8#.UvNkYIZhauY
(badly set qscrollbar)
Ok - problem was caused by the line
setMaximum(myPlot->dataFromSources->lastTimeValMicro()-pageStep());
It was setting the maximum to 0 sometimes. why? I wanted the slider to take up the whole length of the scrollbar so that at the beginning when the plot was shown in 100% view, you couldn't scroll it and I could achieve that by setting the pagestep to my plot's maximum value and the maximum to 0.
But that caused the setValue(int)problem - you can't set a value bigger than the maximum.
So what finally worked for me is:
double valueToSet=myPlot->getLowerBound(QwtPlot::xBottom);
if(valueToSet>maximum())
setMaximum(valueToSet);
setValue(myPlot->getLowerBound(QwtPlot::xBottom));
setPageStep(myPlot->get_X_delta());
setSingleStep(myPlot->get_X_delta()/10);
setMaximum(myPlot->dataFromSources->lastTimeValMicro()-pageStep());
update();

Forcing smaller redraw regions in actionscript

I've got a small action-script chart, that's meant to be live updating, and also be able to support more than 10000 points of data. The way it's currently set up it doesn't need to redraw the whole chart if the new line we wish to add doesn't extend that chart's boundaries.
Yet it does, the redraw regions show the whole chart as being redrawn as opposed to the single line i need to add.
When the chart gets a new piece of data from the javascript it does the following.(some stuff has been stripped for clarity.
private function registerJSCallbacks() : void
{
ExternalInterface.addCallback( "addData", addData );
}
private function addData( val : * ) : void
{
trace( "addData",val );
var g: Graphics = this.graphics;
g.moveTo(x1,y1);
g.lineTo(x2,y2);
}
Is there any better way to do that, that won't redraw the whole screen? Is my coding pattern wrong for this type of update?
I'm a novice so even vaguely relevant advice would be appreciated.
You're editing the underlying vector, so it has to redraw the whole thing. You've got a couple options:
(easiest): Spawn a new Sprite after every X draw operations, so that each draw only recalculated a few vector lines
(more involved): Use one Sprite to draw, and every X draw operations, write the graphics in the sprite to a backing BitmapData object (using bitmapData.draw) and clear the sprite.
Option 2 is probably performs better than option 1, but I haven't benchmarked this specific scenario. You might get comparable performance if you sprite.cacheAsBitmap = true on each Sprite in option 1 as you move to a new "active" sprite.

Comparing bitmap data in AS3 pixel for pixel

I am looking for a fairly simple image comparison method in AS3. I have taken an image from a web cam (with no subject) passed it in to bitmap data, then a second image is taken (this time with a subject) to compare this data, from these two images I would like to create a mask from the pixels that match on both bitmaps. I have been scratching my head for a while, and I am not really making any progress. Could any one point me in the right direction for pixel comparison method, something like getPixel32()
Cheers
Jono
use compare to create a difference between the two and then use treshold to extract the parts that interest you.
edit: actually it is pretty straight forward. the trick is to apply the threshold multiple times per channel using the mask parameter (otherwise the comparison only makes little sense, since 0x010000 (which is almost black) is consider greater than 0x0000FF (which is anything but black)). here's how:
var dif:BitmapData;//your original bitmapdata
var mask:BitmapData = new BitmapData(dif.width, dif.height, true, 0);
const threshold:uint = 0x20;
for (var i:int = 0; i < 3; i++)
mask.threshold(dif, dif.rect, new Point(), ">", threshold << (i * 8), 0xFF000000, 0xFF << (i * 8));
this creates a transparent mask. then the threshold is applied for all three channels, setting the alpha channel to fully opaque where the channels value exceeds the threshold value (you might wanna decrease it).
you can isolate the foreground object ("the guy in front of the webcam") by copying the alpha channel from the mask to the current video image.
one of the problems here is that you want to find if a pixel has ANY change to it, and if it does then to convert that pixel to another color (for masking). Unfortunately, a webcam's quality isn't great so even if your scene does not change at all the bitmapdata coming from the webcam will change slightly. Therefor, when your subject steps into frame...you will get pixel changes for the subject...but also noise in other areas due to lighting changes or camera quality. What you'll need to do is write a function that analyzes the result of a bitmapdaya.compare() for change in area's larger than _____ to determine if there is enough change to warrant an actual object being there. That will help remove noise and make your mask more accurate.

Resources