I am trying to use Qwt in my project. I need to draw a plot which can be replaced by another based on some conditions.
My widget is created with the following code:
QStackedWidget *stackedWidget = new QStackedWidget(ui->m_capacitancesWidget);
for(int i = 0; i < sensors; ++i) {
QwtPlot *plot = new QwtPlot();
m_capacitancesPlots.push_back(plot); // convenience vector for easy access to plots
plot->setTitle(tr("Mutual capacitances"));
plot->setCanvasBackground(Qt::white);
plot->insertLegend(new QwtLegend());
QwtPlotCurve *curve = new QwtPlotCurve();
curve->setTitle(tr("Sensor %1").arg(1));
curve->setPen(Qt::green, 1);
curve->setRenderHint(QwtPlotItem::RenderAntialiased, true);
curve->attach(plot);
// actually I add another two curves which have some data, but I omit it in this snippet
stackedWidget->addWidget(plot);
}
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(stackedWidget);
ui->m_capacitancesWidget->setLayout(layout);
Here is my widget:
Sometimes I am loading new data and want to refresh the widget. Since there could be different number of sensors and so on, I call the following code to clean things up and then the same code above to create the view from scratch:
for(int i = 0; i < m_capacitancesPlots.size(); ++i)
m_capacitancesPlots[i]->detachItems(QwtPlotItem::Rtti_PlotItem, true);
m_capacitancesPlots.clear();
if(ui->m_capacitancesWidget->layout())
delete ui->m_capacitancesWidget->layout();
But after this, my widget looks like this:
Axes are doubled, titles are doubled. Plots seem to be ok, but when I draw the third one (green in legend), it's not visible. It seems that although QStackedWidget and QwtPlot are completely new, I see some cached old QwtPlot. How to properly clean my widget and draw new QwtPlot?
Related
Is it possible to draw a shape with open ends?
E.g.: Let's say I want to draw a tree, which roots are open. Is there a elegant way to let the ends open, without overdrawing the already drawed lines?
I could overdraw it with shapes, which are exactly as big as my openings and have the color of the background, but I don't think that is the elegant way and I don't find any option to let them open. Perhaps I'm just blind and I could make strokePolygon(...) in which not all points are linked, but I think that's neither the way to go.
Let's have a simple shape:
[ceate Scene and Stage, etc]
Canvas sc = new Canvas(x, y);
GraphicsContext gcCs = cs.getGraphicsContext2D();
gcCs.setStroke(Color.BLACK);
double counter = 0.0;
[calculate points, instantiate arrays, etc]
for (int i = 0; i < arrayX.length; i++)
{
arrayX = shapeMidX + Math.cos(Math.toRadiants(counter * Math.PI)) * shapeSizeX / 2);
arrayY = shapeMidY + Math.sin(Math.toRadiants(counter * Math.PI)) * shapeSizeY / 2);
}
gcCs.strokePolygon(arrayX, arrayY, arrayX.length);
[making other things]
stackPane.getChildren().add(sc);
I know that I could use .strokeOval(), but I wanted to have a example that is near of my own code.
I like to draw my shapes from the center.
P.S.: I wrote the for() { } out of my head, it could be that there's something wrong. I've got no Internet at home at the moment, so my answers could be taking a lot of time.
Thank you in advance.
You could draw individual lines using strokeLine and store the current position in variables allowing you to draw any combination of lines.
You could also construct a path instead which allows you to use moveTo instead of lineTo to "skip" a segment. This way you don't need to keep track of the previous position for continuous lines.
The following example draws every other line of a square this way:
#Override
public void start(Stage primaryStage) {
Canvas canvas = new Canvas(400, 400);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.moveTo(100, 100);
gc.lineTo(100, 300);
gc.moveTo(300, 300);
gc.lineTo(300, 100);
// gc.moveTo(100, 100);
gc.stroke();
Scene scene = new Scene(new StackPane(canvas));
primaryStage.setScene(scene);
primaryStage.show();
}
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 a Qwt plot defined in a class method:
plot = new QwtPlot();
const int margin = 5;
plot->setContentsMargins( margin, margin, margin, 0 );
plot->setTitle( "Support polygon" );
plot->setCanvasBackground( Qt::white );
plot->setAxisScale( QwtPlot::yLeft, -0.8,0.8 );
plot->setAxisScale( QwtPlot::xBottom, -0.8,0.8 );
QBoxLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight);
layout->addWidget(plot);
setLayout(layout);
curve_ = new QwtPlotCurve();
curve_->attach( plot );
xData = new double[4];
yData = new double[4];
QTimer *replotTimer_ = new QTimer(this);
connect(replotTimer_, SIGNAL(timeout()), this, SLOT(updateMe_()));
replotTimer_->start(100);
the data is updated when the timer calls the callback function updateMe_() and the plot should be updated
void Support_polygon::updateMe_()
{
curve_->setRawSamples(xData,yData,4);
plot->replot();
}
xData and yData are also being modified in a thread, whose callback function is:
void Support_polygon::callback_()
{
msg_mutex.lock();
for (size_t ii=0; ii<msg.contacts.size(); ii++)
{
xData[ii] = 1.4f*float(std::rand())/float(RAND_MAX)-0.7;
yData[ii] = 1.4f*float(std::rand())/float(RAND_MAX)-0.7;
}
msg_mutex.unlock();
}
(right now I'm only putting random numbers, but when this works the data will be passed by a ROS message, that's why is in a different thread)
The problem is that the plot is never updated. As if replot() is never called. I tested and all callback functions are being called.
Surprisingly, the plot gets updated if I resize the window... if I keep re-sizing, the plot gets updated while I do it.
The replot call is being done on the main thread by the timer timeout signal. I don't understand what is going on.
Even though in the documentation it says that setAutoReplot is not recommended and that performance wise is better to use replot, I tested setting setAutoReplot to true
plot->setAutoReplot( true );
removed the replot call in updateMe_(), and everything works.
But to me this seems like a bug.
Note: I'm using Qwt 6.1.2.
note: after many more issues with qwt I switched to qtcustomplot. Only one header and one cpp file. My two cents in case anyone is looking for a substitute.
I created two QGLwidget objects:
slicerCanvas* slicerX_;
secCan* secWid_;
secWid_ = new secCan(this);
//slicerX_ load data and create texture object share with secWid_
slicerX_ = new slicerCanvas(this,secWid_);
and in the main window, I use QHBoxLayout:
hBox_ = new QHBoxLayout;
hBox_->addWidget(slicerX_);
hBox_->addWidget(secWid_);
centerWidget_->setLayout(hBox_);
It works right, but if I addWidget like this:
hBox_ = new QHBoxLayout;
hBox_->addWidget(secWid_);//first
hBox_->addWidget(slicerX_);//second
centerWidget_->setLayout(hBox_);
The secWid_ will not paint anything, only slicerX_ paints correctly. I am confused as to why.
I have a QGraphicsScene of big dimension for displaying a database content.
Part of the database is made of pictures that I place in the QGraphicsScene thanks to the method setPos() of a QGraphicsPixmapItem and this works fine with thousands of pictures.
In front of these pictures, I place QCheckboxes that are finally accessible through QGraphicsProxyWidgets. But QGraphicsProxyWidget::setPos(qreal x, qreal y) results in casting provided coordinates in signed short in the QGraphicsScene.
However, doing a QGraphicsProxyWidget::pos() returns correctly the original coordinates, even above 2^16.
Here is the code:
QCheckBox* checkbox = new QCheckBox("", this);
QWidget* dummyWidget = new QWidget; //used for having a transparent background
dummyWidget->setStyleSheet("background-color:transparent;"
"outline-color:transparent;"
"font-size: 8pt;");
QHBoxLayout* dummyLayout = new QHBoxLayout(dummyWidget);
dummyLayout->addWidget(checkbox);
QGraphicsProxyWidget* proxyWidget = scene.addWidget(dummyWidget);
proxyWidget->setPos(0, 120*i);
When 120*i is between 32769 and 65536, QChekBoxes don't show. For above values, QCheckBoxes are shown like if y = value - 65536.
I have tried many things without success, like
- proxyWidget->moveBy
- dummyWidget->move
- dummyWidget->setFixedSize(0, 240*i); checkbox->move(0, 120*i);
Any solution?
PS: The toolchain/cross-toolchain I depend from embeds QT4.8.1. for the desktop side.
I have no way to change that so upgrading to QT5.x is not an option.
You can use next trick:
void setNewPos(QGraphicsItem *item, QPointF pos)
{
item->resetTransform();
QTransform trans = item->transform();
item->setTransform(trans.translate(pos.x(), pos.y()));
}
Now, you can call this func:
QPushButton *btn = new QPushButton("Hello, people!");
QGraphicsProxyWidget *wdgItem = scene->addWidget(btn);
setNewPos(wdgItem, view->mapToScene(0,0)); // There's scenePos can have any coords