I am trying to create a timeline app for visualization of certain time-sensitive requirements information and have begun experimentation with Qt since I am new to it. The problem I am having is that the rendered object will show up if I render it just basically on the main background, but when I tried to add the TimeLine objects to the vertical layout I have on my UI they refuse to render.
Here is my main window code for creating the objects:
void MainWindow::drawTimelines()
{
for(int i=0; i <= 3; i++)
{
TimeLine *tl = new TimeLine(this, this);
// ui->verticalLayout->addWidget(tl, 0, Qt::AlignLeft);
tl->lower();
QPoint * dest = new QPoint(this->width() - (this->width() / 12), i * this->height() / 4 + this->height() / 4);
QPoint * src = new QPoint(this->width() / 12, i * this->height() / 4 + this->height() / 4);
// tl->setGeometry(0, 0, this->width(), 100);
tl->setGeometry(src->x(), src->y(), this->width(), 100);
tl->updateGeometry();
QPoint *startPt = new QPoint(src->x(), 50);
QPoint *endPt = new QPoint(dest->x(), 50);
// QPoint *src = new QPoint(this->width() /8, 50);
// QPoint *dest = new QPoint(this->width() - (this->width() / 8), 50);
tl->setSrcPt(startPt);
tl->setDestPt(endPt);
tl->setNumSegments(3);
timeLineList.push_back(tl);
timeLineList.at(i)->show();
}
update();
}
Here is the maint function present in the TimeLine object itself:
void TimeLine::paintEvent(QPaintEvent * event)
{
QRectF frame(QPointF(sourcePoint->x(), sourcePoint->y()), geometry().size());
QPainter painter(this);
painter.setPen(QPen(Qt::FlatCap));
painter.drawRoundedRect(frame, 10.0, 10.0);
int translateAmount = sourcePoint->y() - window->getPainterY();
painter.translate(0, translateAmount);
// window->setPainterY(translateAmount);
painter.drawLine(sourcePoint->x(), 25, destPoint->x(), 25);
for(int i = 0; i <= numSegments; i++){
int xPoint = ((destPoint->x() - sourcePoint->x()) * i / numSegments) + sourcePoint->x();
int yPoint = 25;
painter.drawLine(xPoint, yPoint + 20, xPoint, yPoint - 20);
}
QWidget::paintEvent(event);
}
For reference, the verticalLayout is meant to have one
The TimeLines (and bounded rect boxes) do not render when I have the
// ui->verticalLayout->addWidget(tl, 0, Qt::AlignLeft);
uncommented. As you can see from a bunch of the other commented lines, I have tried numerous other things to try and render these TimeLines. I have tried:
changing reference points of the geometry to be (0,0) for the new segment of the vertical layout
changing the size of the geometry
both translating and not translating the painter
changing line thickness, type of line, etc.
even tried rendering something else simple in the vertical layout
The part that confuses me is that even the bounded rect made based on the geometry of the TimeLine frame gets cut off on the side and top of the timeline (it only shows top left corner and the top and left side-lines) even when rendered on the normal screen.
Related
I want to draw a line to connect two circles (QGraphicsEllipseItem), but I find that I don't get the desired result with this way of writing.
//they have been initialized to the correct place
QGraphicsEllipseItem* nodeu;
QGraphicsEllipseItem* nodev;
this->addLine(nodeu->x(), nodeu->y(), nodev->x(), nodev->y());
The result of executing these codes is that only two circles appear, but no lines appear.
like this
My rough inference is the problem of coordinate transformation, but I just can't solve it.
thank you!
you should first add one QGraphicsView in your UI or :
QGraphicsView *graphicsView;
QGridLayout *gridLayout;
gridLayout = new QGridLayout(centralwidget);
gridLayout->setSpacing(0);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
graphicsView = new QGraphicsView(centralwidget);
graphicsView->setObjectName(QString::fromUtf8("graphicsView"));
gridLayout->addWidget(graphicsView, 0, 0, 1, 1);
then :
QGraphicsScene *_scene = new QGraphicsScene(this);
ui->graphicsView->setScene(_scene);
ui->graphicsView->setRenderHints(QPainter::Antialiasing);
QGraphicsEllipseItem *nodeu = new QGraphicsEllipseItem;
nodeu->setRect(20, 10, 20, 20);
_scene->addItem(nodeu);
QGraphicsEllipseItem *nodev = new QGraphicsEllipseItem;
nodev->setRect(80, 60, 20, 20);
_scene->addItem(nodev);
QGraphicsLineItem *_lineItem = new QGraphicsLineItem;
_lineItem->setLine(nodeu->rect().x() + nodeu->rect().width() / 2.0, nodeu->rect().y() + nodeu->rect().height() / 2.0,
nodev->rect().x() + nodev->rect().width() / 2.0, nodev->rect().y() + nodev->rect().height() / 2.0);
_scene->addItem(_lineItem);
this is the output:
I would like to make an application which includes rotation widget inside a circle. I started this with the AnalogClock example in qt. I wouldd like to rotate a quarter pie (quarter circle) instead of gauge. My problem is I cannot locate the pie in the center of my circle.
Below picture, I barely locate the pi in the center of circle but I would like to make the pi bigger than below picture.
enter image description here
I realized that all paintings locate inside of rectangle like below.
enter image description here
When I change the drawPie function parameters rectangle's location change and rotates based on on edge point. I can make the ractange bigger but this time drawPie center changes too like the last visual.
enter image description here
You can check the code below. It is modifed from the AnalogClock example. I would like to draw a quarter pie and make it the rotate endlessly. Any help will be appreciated. If you have a better opinion, I would like hear that too.
AnalogClock::AnalogClock(QWidget *parent) :
QWidget(parent),
ui(new Ui::AnalogClock)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
timer->start(1000);
setWindowTitle(tr("Analog Clock"));
resize(200, 200);
}
void AnalogClock::paintEvent(QPaintEvent *)
{
QColor minuteColor(0, 127, 127, 191);
int side = qMin(width(), height());
QTime time = QTime::currentTime();
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(width() / 2, height() / 2);
painter.scale(side / 200.0, side / 200.0);
painter.setPen(Qt::NoPen);
qreal radius=10;
qreal startAngle=0;
qreal span=60;
for (int i = 0; i < 12; ++i) {
painter.drawLine(88, 0, 96, 0);
painter.rotate(30.0);
}
painter.setBrush(minuteColor);
painter.save();
painter.rotate(6.0 * time.second() );
//painter.drawConvexPolygon(minuteHand, 3);
QRect rect( -radius, -radius, radius*10, radius*10);
painter.drawPie( rect, startAngle*16, span*16 );
// painter.fillRect(rect,QBrush(Qt::green));
painter.restore();
painter.setPen(minuteColor);
for (int j = 0; j < 60; ++j) {
painter.drawLine(92, 0, 96, 0);
painter.rotate(6.0);
}
}
The drawPie() function takes a rectangle that defines the hypothetical full circle in which the pie (slice) will be painted. In this example, we want that circle to be the same as the full clock circle - same size, same center.
Now the code has normalized the coordinate system for us, so that the clock center point is at (0, 0), and the radius is 100. So we need a rectangle that has its center point in (0,0) and sides that are 2*radius long. That is:
QRect rect(-100, -100, 200, 200);
Changing the rect in the code sample to that fixes the basic issue.
My goal is to draw on QLabel with every QTimer emission.
Here's how i'm trying to achieve this:
so, timer is created by triggering an action.
I want measures, parameters of which are updated as user inputs data to the dialog, to be drawn with every timer emission.
void ImageViewer::on_measuresAct_triggered()
{
dialog = new Measures;
dialog->show();
Ymax = Origin = Xmax = QPoint(30,30);
measuresflag = true;
pixtemp = imageLabel->pixmap();
timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(drawontimer()));
timer->start(100);
if(!dialog->isVisible())
timer->stop();
}
Here's the slot that is to draw the measures.
void ImageViewer::drawontimer()
{
pixmap = pixtemp;
qDebug()<<"Hey there";
pix = (*pixmap);
QPainter paint(&pix);
QPen MeasurePen (Qt::magenta);
MeasurePen.setWidth(5);
QBrush MeasureBrush (Qt::magenta,Qt::SolidPattern);
paint.setPen(MeasurePen);
paint.setBrush(MeasureBrush);
paint.drawLine(Ymax,Origin);
paint.drawLine(Origin, Xmax);
QString originpoint = "(" + dialog->ui->olinex->text() + ", " + dialog->ui->oliney->text() + ")";
QString xmaxpoint = "(" + dialog->ui->xline->text() + ", " + dialog->ui->oliney->text() + ")";
QString ymaxpoint = "(" + dialog->ui->yline->text() + ", " + dialog->ui->olinex->text() + ")";
paint.setPen(QPen(Qt::green));
paint.setFont(QFont("Arial", 15, QFont::Bold));
paint.drawText(Origin.x() + 8, Origin.y() + 18, originpoint);
paint.drawText(Xmax.x() - 10, Xmax.y() - 8, xmaxpoint);
paint.drawText(Ymax.x() + 8, Ymax.y() + 8, ymaxpoint);
QPolygon poly1, poly2;
poly1 << Xmax << QPoint(Xmax.x() - 12, Xmax.y() - 6)
<< QPoint(Xmax.x() - 12, Xmax.y() + 6)<< Xmax;
poly2 << Ymax << QPoint(Ymax.x() + 6, Ymax.y() + 12) << QPoint(Ymax.x() - 6, Ymax.y() + 12) << Ymax;
// style(), width(), brush(), capStyle() and joinStyle().
QPen ArrowPen(Qt::magenta, 1);
paint.setPen(ArrowPen);
// Brush
QBrush brush;
brush.setColor(Qt::magenta);
brush.setStyle(Qt::SolidPattern);
// Fill polygon
QPainterPath path1, path2;
path1.addPolygon(poly1);
path2.addPolygon(poly2);
// Draw polygon
paint.drawPolygon(poly1);
paint.fillPath(path1, brush);
paint.drawPolygon(poly2);
paint.fillPath(path2, brush);
imageLabel->setPixmap(pix);
}
But my program crashes. I figured out where my problem is by using "Hey there" message output with QDebug. It's in
pix = (*pixmap);
line of my code. If i comment this line program doesn't crash, nothing is drawn neither.
Declaration in .h file:
const QPixmap* pixmap;
QPixmap pix;
const QPixmap * pixtemp;
"Hey there" message is written 2 times most of the time, but 1 time out of 10 it's written 3 or 4 times. That confuses even more.
So, the problem is how i draw on a label (i suppose). Is there more convinient or "right" way to draw on label, which won't lead to crash of my program.
Thanks in advance!
Do not try to draw into QLabel's pixmap or mangling with the pointers. Instead:
Create a new pixmap p on stack (local variable, no pointer) using the copy constructor
Check if your pixmap is valid (p.isNull())
Draw onto the pixmap p
Call setPixmap(p) on your label
A general note on Qt properties: Qt properties are not to be modified directly. The access to Qt properties always works through getting and setting. So, if you want to manipulate a property, you initialize a local variable with the getter, then modify your local variable, then use the setter.
A note about drawing into widgets: If you don't want to work on a copy, you can draw on a widget directly by deriving and implementing the paint() method. Furthermore, you can call update() on the widget to trigger a repaint (which will call your paint() method). This will be more efficient than using a label with a pixmap.
In QT I have a qvtkWidget in which I plot a graph.
I use two functions:
The following function initializes the plot with two points (0,0) and (0,1):
bool VTKPlotter::initPlot(){
// Create a table:
plotTable = vtkSmartPointer<vtkTable>::New();
vtkSmartPointer<vtkFloatArray> arrT = vtkSmartPointer<vtkFloatArray>::New();
vtkSmartPointer<vtkFloatArray> arrUSD = vtkSmartPointer<vtkFloatArray>::New();
arrT->SetName("Time");
plotTable->AddColumn(arrT);
arrUSD->SetName("USD");
plotTable->AddColumn(arrUSD);
//Set up the view:
view = vtkSmartPointer<vtkContextView>::New();
view->GetRenderer()->SetBackground(0.8, 0.8, 0.8);
//Set up chart:
vtkSmartPointer<vtkChartXY> chart = vtkSmartPointer<vtkChartXY>::New();
view->GetScene()->AddItem(chart);
//Set renderer, interactor:
view->SetInteractor(plot_qvtkWidget->GetInteractor());
plot_qvtkWidget->SetRenderWindow(view->GetRenderWindow());
plot_qvtkWidget->show();
//Add line and initial values:
vtkPlot *line;
line = chart->AddPlot(vtkChart::LINE);
line->SetInputData(plotTable, 0, 1);
line->SetColor(25, 25, 230, 220);
line->SetWidth(1.5);
int numPoints = 2;
plotTable->SetNumberOfRows(numPoints);
plotTable->SetValue(0, 0, 0);
plotTable->SetValue(1, 0, 1);
plotTable->SetValue(0, 1, 0);
plotTable->SetValue(1, 1, 1);
return true;
}
where plotTable is a vtkSmartPointer<vtkTable>, view is vtkSmartPointer<vtkContextView> and plot_qvtkWidget is a QVTKWidget*.
When the above function is called, the plot is immediately shown in the widget.
Then I have this function which is called when I click a certain button in my QT application:
void VTKPlotter::addValue(double v){
plotTable->InsertNextBlankRow();
int r = plotTable->GetNumberOfRows() - 1;
plotTable->SetValue(r, 0, r);
plotTable->SetValue(r, 1, v);
}
The problem is, when I call the above function, the plot is not immediately updated. If I zoom in and out and pan around the plot, it eventually updates, and I can see the new point.
I've tried these methods:
view->ResetCamera();
view->Update();
view->Render();
view->GetRenderWindow()->Render();
view->GetRenderer()->ResetCamera();
view->ResetCameraClippingRange();
plot_qvtkWidget->update();
How do I force my plot to update?
One of possible solution is to call chart->ClearPlots() with subsequent appropriate calls chart->AddPlot(...)
I am working through Greg Borenstein's book "Making Things See" and have figured out how to create a cursor that tracks the movement of the closest thing to the Kinect. Right now the cursor is a simple red ball. So I am able to track my finger over
image(kinect.getVideoImage(), 0, 0)
I have also created buttons that apply a filter to the video image when I put the cursor ball in the area of the button.
Its kind of fun but the novelty has run out so now I want to turn the cursor ball into an animated graphic using particles or something fun like that. This animated graphic should still track my finger and be drawn over the video image.
When I try to write this, the graphic comes out wrong because the video image keeps redrawing over the particles so it doesn't look right.
I was thinking I could use the capture() method to draw the video image under the graphics but I can't figure out how to do it with video from the Kinect.
Does anyone have any ideas on how I could do this? Any help would be greatly appreciated.
A sample of my kinect tracker and filter button is below. If you copy and paste it into processing and have a kinect plugged in it should run. my apologies for the code's lack of eloquence. I'm still learning how to make beautiful code.
Instead of filters, I would like to trigger a new graphic for the red ball maybe apply particles or something.
//my kinect tracker
import org.openkinect.freenect.*;
import org.openkinect.processing.*;
Kinect kinect;
boolean ir = true;
boolean colorDepth = true;
boolean mirror = true;
float closestValue;
float closestX;
float closestY;
// create arrays to store recent closest x- and y-coordinates for averaging
int[] recentXValues = new int[3];
int[] recentYValues = new int[3];
// keep track of which is the current value in the array to be changed
int currentIndex = 0;
float circleButtonX, circleButtonY; // position of circle button
float circleButtonSize; // diameter of circle button
color circleButtonColor; // color of circle button
void setup() {
size(640, 480, P3D);
kinect = new Kinect(this);
kinect.initDepth();
kinect.initVideo();
//kinect.enableIR(ir);
kinect.enableMirror(mirror);
kinect.enableColorDepth(colorDepth);
circleButtonColor = color(0, 0, 255);
}
void draw() {
closestValue = 1700;
int[] depthValues = kinect.getRawDepth();
for(int y = 0; y < 480; y++) {
for(int x = 0; x < 640; x++) {
int i = x + y * 640;
int currentDepthValue = depthValues[i];
if(currentDepthValue > 0 && currentDepthValue < closestValue) {
//save its value
closestValue = currentDepthValue;
recentXValues[currentIndex] = x;
recentYValues[currentIndex] = y;
}
}
}
currentIndex++;
if(currentIndex > 2) {
currentIndex = 0;
}
// closetX and ClosestY become a running average
// with currentX and CurrentY
closestX = (recentXValues[0] + recentXValues[1] + recentXValues[2]) / 3;
closestY = (recentYValues[0] + recentYValues[1] + recentYValues[2]) / 3;
//draw the depth image on the screen
image(kinect.getVideoImage(), 0, 0);
fill(0, 0, 250);
ellipse(75, 75, 100, 100);
ellipse(200, 75, 100, 100);
ellipse(75, 200, 100, 100);
rect(540, 25, 75, 100);
//buttons
fill(255,0,0);
textSize(24);
text("Invert", 40, 85);
text("Blur", 50, 210);
textSize(18);
text("Threshold", 155, 85);
text("Stop", 560, 75);
ellipse(closestX, closestY, 25, 25);
if (closestX > 25 && closestX < 125 && closestY > 25 && closestY < 125) {
filter(INVERT);
};
if (closestX > 150 && closestX < 250 && closestY > 25 && closestY < 125) {
filter(THRESHOLD);
};
if (closestX > 25 && closestX < 125 && closestY > 150 && closestY < 250) {
filter(BLUR, 6);
};
if (closestX > 540 && closestX < 615 && closestY > 25 && closestY < 100) {
noLoop();
loop();
background(0);
};
I believe you're asking how to create a particle trail in Processing. This doesn't really have anything to do with kinect- a simple call to background() would also draw over any of your particles. So you currently have something like this:
void setup(){
size(500, 500);
}
void draw(){
background(0);
ellipse(mouseX, mouseY, 10, 10);
}
You're drawing something to the screen (in your code, a red ball, in my code, an ellipse), but it's cleared away (in your code, the kinect video, in my code, a call to background(0)). You're asking how to make it so the ellipse stays on the screen even after you draw the background.
The answer is this: You need to store the positions of your trail in a data structure and then redraw them every frame.
A simple way to do that is to create an ArrayList of PVector instances. To add a particle to the trail, you just add a PVector to the ArrayList. And then you just iterate over the trail and draw every PVector point.
ArrayList<PVector> trail = new ArrayList<PVector>();
void setup(){
size(500, 500);
}
void draw(){
background(0);
//add to the trail
trail.add(new PVector(mouseX, mouseY));
//draw the trail
for(PVector p : trail){
ellipse(p.x, p.y, 10, 10);
}
}
However, this trail will constantly grow, and you'll eventually run out of memory to hold every point. So to keep your trail from doing that, you need to limit its size:
ArrayList<PVector> trail = new ArrayList<PVector>();
void setup(){
size(500, 500);
}
void draw(){
background(0);
//add to the trail
trail.add(new PVector(mouseX, mouseY));
if(trail.size() > 10){
//trail is too long, remove the oldest point
trail.remove(0);
}
//draw the trail
for(PVector p : trail){
ellipse(p.x, p.y, 10, 10);
}
}
From here you can do fancier things: give each point in your trail a momentum or a color, or fade it out, or decrease its width. But the basics are this: you have to store the particles in a data structure, update that data structure to add or remove particles, and then iterate over that data structure to draw the trail.