I am trying to detect when a Label is clicked/pointed/hit. I come from programming in Win32 C++ & Java Swing and I know both take different approaches to how you register for event/input.
I have looked at the tutorials but I cannot find an example of detecting a click. Is there a constant for a click, so then I can detect it in the keyPressEvent (Ie, like win32 & the WM_LBUTTONDOWN)? Or do I need to first register for the click then call my own function to handle the click (like Java & .addActionListener())?
My attempts to detect the click below dont work:
#include <MAUtil/Moblet.h>
#include <MAUI/Layout.h>
#include <MAUI/ListBox.h>
#include <MAUI/Label.h>
#include <MAUI/EditBox.h>
#include <MAUI/Screen.h>
#include <MAUtil/Environment.h>
#include <madmath.h>
#include <conprint.h>
using namespace MAUtil;
using namespace MAUI;
class MouseScreen : public Screen, public PointerListener
{
private:
Label *testLabel;
public:
MouseScreen()
{
MAExtent screenDim = maGetScrSize();
Layout* mainLayout = new Layout( 0, 0, EXTENT_X(screenDim), EXTENT_Y(screenDim), NULL, 1, 3 );
ListBox* mainListBox = new ListBox( 0, 0, 100, 200, mainLayout,
ListBox::LBO_VERTICAL, ListBox::LBA_LINEAR,
true );
mainListBox -> setPaddingLeft( 10 );
mainListBox -> setPaddingRight( 10 );
mainListBox -> setPaddingTop( 10 );
mainListBox -> setPaddingBottom( 10 );
mainListBox -> setBackgroundColor( 900 );
mainLayout -> setBackgroundColor( 300 );
testLabel = new Label( 10, 300, 50, 20, mainLayout );
//testLabel -> addPointerListener( this );
testLabel -> setCaption( "Click me" );
mainLayout -> add( testLabel );
}
void pointerPressEvent( MAPoint2d p )
{
printf( "clicked" ); // never occurs
// OR
if ( testLabel.contains((MouseScreen*)p) )
{
printf( "Label clicked" );
}
// Should I call parent function
// PointerListener :: pointerPressEvent( p );
}
void pointerMoveEvent( MAPoint2d p ) {}
void pointerReleaseEvent( MAPoint2d p ) {}
};
class MouseMoblet : public Moblet
{
public:
MouseMoblet()
{
instance = new MouseScreen();
instance -> show();
}
~MouseMoblet()
{
delete instance;
}
void keyPressEvent(int keyCode, int nativeCode)
{
// todo: handle key presses
printf( "Blah" ); // never occurs when I press the mouse, but other KEYS work
}
void keyReleaseEvent(int keyCode, int nativeCode)
{
// todo: handle key releases
}
private:
MouseScreen *instance;
};
extern "C" int MAMain()
{
Moblet::run(new MouseMoblet());
return 0;
};
I can see some things that you need to do. First, you need to set mainLayout as the main widget for Screen by calling setMain(mainLayout). This makes the screen aware of the mainLayout so that it can draw it. Once you have done this you will be able to see your widgets on the screen and you should also get click events.
In pointerPressedEvent you were almost right, the contains method of testLabel takes a point rather than a Screen. What you should do here is to call testLabel->contains( p.x, p.y ) to evaluate if the testLabel was clicked.
The full revised code looks something like this:
#include <MAUtil/Moblet.h>
#include <MAUI/Layout.h>
#include <MAUI/ListBox.h>
#include <MAUI/Label.h>
#include <MAUI/EditBox.h>
#include <MAUI/Screen.h>
#include <MAUtil/Environment.h>
#include <madmath.h>
#include <conprint.h>
using namespace MAUtil;
using namespace MAUI;
class MouseScreen : public Screen
{
private:
Label *testLabel;
public:
MouseScreen()
{
MAExtent screenDim = maGetScrSize();
Layout* mainLayout = new Layout( 0, 0, EXTENT_X(screenDim), EXTENT_Y(screenDim), NULL, 1, 3 );
ListBox* mainListBox = new ListBox( 0, 0, 100, 200, mainLayout,
ListBox::LBO_VERTICAL, ListBox::LBA_LINEAR,
true );
mainListBox -> setPaddingLeft( 10 );
mainListBox -> setPaddingRight( 10 );
mainListBox -> setPaddingTop( 10 );
mainListBox -> setPaddingBottom( 10 );
mainListBox -> setBackgroundColor( 900 );
mainLayout -> setBackgroundColor( 300 );
testLabel = new Label( 10, 300, 50, 20, mainLayout );
//testLabel -> addPointerListener( this );
testLabel -> setCaption( "Click me" );
mainLayout -> add( testLabel );
setMain( mainLayout );
}
void pointerPressEvent( MAPoint2d p )
{
if ( testLabel->contains( p.x, p.y ) )
{
printf( "Label clicked" );
}
}
void pointerMoveEvent( MAPoint2d p ) {}
void pointerReleaseEvent( MAPoint2d p ) {}
};
class MouseMoblet : public Moblet
{
public:
MouseMoblet()
{
instance = new MouseScreen();
instance -> show();
}
~MouseMoblet()
{
delete instance;
}
void keyPressEvent(int keyCode, int nativeCode)
{
// todo: handle key presses
printf( "Blah" ); // never occurs when I press the mouse, but other KEYS work
}
void keyReleaseEvent(int keyCode, int nativeCode)
{
// todo: handle key releases
}
private:
MouseScreen *instance;
};
extern "C" int MAMain()
{
Moblet::run(new MouseMoblet());
return 0;
};
Note: The current MoSync UI system was designed for non-touch phones, so it is a bit contrived to handle pointer events, this will however be improved in the coming release.
Regarding wether you should call the parent function or not depends on if you want to keep the default behavior or not, currently the default implementation of Screen does nothing.
Related
I have a QwtPlot view containing many QwtPlotCurve and I want to highlight/mignify (currently simply trying to change the color) of the closest point to the mouse position (because I'll display some info about this point of measurement when user will press the mouse button, and I'd like him to know what point is the current "target").
So I use a QwtPlotPicker to get mouse position and then I setup an extra QwtPlotCurve curve with this single point ("target") to be drawn with a different color on top of the others.
It works, but the only way I could make this work is by calling QwtPlot::replot() which is heavy to be called every time the mouse is being moved (as I may have many thousands of point being plotted).
I'd like to only repaint the area where previously highlighted point was (to restore default display) and then only repaint the area where newly highlighted point is. But when I do, this (call repaint(QRect) rather than replot()), nothing happens (no point is highlighted), however, if I deactivate the window, I see the point gets highlighted, so it looks like repaint does some piece of job but not enough for the end user to see it...
Note that I disabled Qwt backing store features.
Here is my MCVE:
widget.h:
#include <QDialog>
class QLabel;
class QwtPlotCurve;
class QwtPlot;
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog();
public slots:
void onHovered( const QPointF& pt );
private:
std::vector<QwtPlotCurve*> curves;
QwtPlotCurve* highlight;
std::tuple<QwtPlotCurve*,int,QRect> highlighted;
QLabel* closestLabel;
QwtPlot* plot;
};
widget.cpp:
#include "widget.h"
#include <QVBoxLayout>
#include <QLabel>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_picker.h>
#include <qwt_plot_canvas.h>
#include <qwt_picker_machine.h>
#include <sstream>
Dialog::Dialog()
{
setLayout( new QVBoxLayout() );
plot = new QwtPlot(this);
layout()->addWidget( plot );
layout()->addWidget( closestLabel = new QLabel( this ) );
for ( int i = 0; i != 5; ++i )
{
QwtPlotCurve* curve = new QwtPlotCurve();
QVector<double> x, y;
for ( int i = 0; i != 10; ++i )
{
x.push_back( std::rand() );
y.push_back( std::rand() );
}
curve->setSamples( x, y );
curve->setStyle( QwtPlotCurve::Dots );
curve->setPen( Qt::black, 5 );
curve->attach(plot);
curves.push_back( curve );
}
highlight = new QwtPlotCurve();
highlight->setSamples( {}, {} );
highlight->setStyle( QwtPlotCurve::Dots );
highlight->setPen( Qt::red, 5 );
highlight->attach(plot);
QwtPlotCanvas* canvas = dynamic_cast<QwtPlotCanvas*>( plot->canvas() );
if ( canvas )
canvas->setPaintAttribute( QwtPlotCanvas::BackingStore, false );
plot->replot();
QwtPlotPicker* picker = new QwtPlotPicker( plot->canvas() );
picker->setStateMachine(new QwtPickerTrackerMachine());
connect(picker, SIGNAL(moved(const QPointF&)), this, SLOT(onHovered(const QPointF&)));
}
// inspired from QwtPlotCurve::closestPoint
int closestPoint( QwtPlotCurve& curve, const QPoint &pos, double *dist )
{
const size_t numSamples = curve.dataSize();
if ( curve.plot() == NULL || numSamples <= 0 )
return -1;
const QwtSeriesData<QPointF> *series = curve.data();
const QwtScaleMap xMap = curve.plot()->canvasMap( curve.xAxis() );
const QwtScaleMap yMap = curve.plot()->canvasMap( curve.yAxis() );
const double xPos = xMap.transform( pos.x() );
const double yPos = yMap.transform( pos.y() );
int index = -1;
double dmin = DBL_MAX;
for ( uint i = 0; i < numSamples; i++ )
{
const QPointF sample = series->sample( i );
const double cx = xMap.transform( sample.x() ) - xPos;
const double cy = yMap.transform( sample.y() ) - yPos;
const double dist = sqrt( pow(cx,2) + pow(cy,2) );
if ( dist < dmin )
{
index = i;
dmin = dist;
}
}
if ( dist )
*dist = dmin;
return index;
}
void Dialog::onHovered( const QPointF& pt )
{
// mouse moved!
QwtPlotCurve* closest = NULL;
int closestIndex = -1;
double minDist = DBL_MAX;
for ( auto curve : curves )
{
double dist;
int index = closestPoint( *curve, pt.toPoint(), &dist );
if ( dist < minDist )
{
minDist = dist;
closestIndex = index;
closest = curve;
}
}
if ( !closest )
return;
std::stringstream str;
QPointF closestPoint = closest->sample(closestIndex);
str << "Closest point is " << closestPoint.rx() << "," << closestPoint.ry();
closestLabel->setText( str.str().c_str() );
if ( std::get<0>( highlighted ) == closest &&
std::get<1>( highlighted ) == closestIndex )
{
// highlighted point is unchanged
return;
}
else
{
// highlighted point changed
const QwtScaleMap xMap = plot->canvasMap( QwtPlot::xBottom );
const QwtScaleMap yMap = plot->canvasMap( QwtPlot::yLeft );
const int rectSize = highlight->pen().width() * 2;
const int x = xMap.transform( closestPoint.rx() );
const int y = xMap.transform( closestPoint.ry() );
const QRect cr = plot->canvas()->contentsRect();
highlight->setSamples( { closestPoint.rx() }, { closestPoint.ry() } );
QRect smallCR( x - rectSize/2, y - rectSize/2, rectSize, rectSize );
std::tuple<QwtPlotCurve*,int,QRect> newHighlighted{ closest, closestIndex, smallCR };
QwtPlotCanvas* canvas = dynamic_cast<QwtPlotCanvas*>( plot->canvas() );
if ( canvas )
{
if ( std::get<2>( highlighted ) != QRect() )
{
// repaint previously highlighted area:
canvas->repaint( std::get<2>( highlighted ) );
}
// repaint newly highlighted area:
canvas->repaint( std::get<2>( newHighlighted ) );
// if you replace lines above by this one, it works!
//canvas->replot();
}
highlighted = newHighlighted;
}
}
main.cpp:
#include <QApplication>
#include "widget.h"
int main( int argc, char* argv[] )
{
QApplication app( argc, argv );
Dialog dlg;
dlg.show();
return app.exec();
}
Edit:
If I replace highlight = new QwtPlotCurve(); by highlight = new MyCurve(); with MyCurve defined as:
class MyCurve : public QwtPlotCurve
{
public:
void drawSeries( QPainter *painter,
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
const QRectF &canvasRect, int from, int to ) const override
{
static int i = 0;
if ( dataSize() != 0 )
std::cout << "PAINTING " << i++ << std::endl;
QwtPlotCurve::drawSeries( painter, xMap, yMap, canvasRect, from, to );
}
};
Then I see that the console show a new "PAINTING" when each canvas->repaint are called, howevere the red point does not become visible. Now if I move another window over mine (or press Alt), a new "PAINTING" is reported and this time the closest point becomes red. So as I mentioned, the method looks good but not enough to have the view be repainted as expected...
You should use QwtPlotDirectPainter, it is designed to do exactly what you want:
QwtPlotDirectPainter offers an API to paint subsets ( f.e all
additions points ) without erasing/repainting the plot canvas.
You can se it being used in the "event_filter" example of Qwt:
// Hightlight the selected point
void CanvasPicker::showCursor( bool showIt )
{
if ( !d_selectedCurve )
return;
QwtSymbol *symbol = const_cast<QwtSymbol *>( d_selectedCurve->symbol() );
const QBrush brush = symbol->brush();
if ( showIt )
symbol->setBrush( symbol->brush().color().dark( 180 ) );
QwtPlotDirectPainter directPainter;
directPainter.drawSeries( d_selectedCurve, d_selectedPoint, d_selectedPoint );
if ( showIt )
symbol->setBrush( brush ); // reset brush
}
Depending on the showIt parameter, this function will either draw the point as "selected" or redraw it in its original/unselected style.
You can see how it is used in the select() function:
void CanvasPicker::select( const QPoint &pos )
{
[...]
showCursor( false ); // Mark the previously selected point as deselected
d_selectedCurve = NULL;
d_selectedPoint = -1;
if ( curve && dist < 10 ) // 10 pixels tolerance
{
d_selectedCurve = curve;
d_selectedPoint = index;
showCursor( true ); // Mark the new point as selected.
}
}
In you case, I believe you could directly use the CanvasPicker class and just do some fine tuning like calling select() on QEvent::MouseMove instead of QEvent::MouseButtonPress.
In the included code I've created an application where I periodically update a label. When the application first starts up, updating the timeLabel results in redrawing the entire contents of the application. This can be observed by running the application with the --gtk-debug=updates argument.
When the button on the right side is clicked, the frame that encloses the contents of the window is removed from the widget hierarchy. This results in further updates to the timeLabel only redrawing the label, and not redrawing swapButton.
Why does a frame seem to want to redraw itself even if it doesn't need to?
#include <gtkmm.h>
class MyWindow
: public Gtk::Window
{
public:
MyWindow();
private:
bool timeout();
void toggleUseOfFrame();
Gtk::Frame frame;
Gtk::Label timeLabel;
Gtk::Button swapButton;
Gtk::Box box;
};
MyWindow::MyWindow()
{
// Layout widgets in initial configuration.
box.pack_start( timeLabel, true, true );
box.pack_start( swapButton, true, true );
box.set_homogeneous();
frame.add( box );
add( frame );
show_all();
set_size_request( 100, 50 );
// Setup signal handlers.
Glib::MainContext::get_default()->signal_timeout().connect(
sigc::mem_fun( *this, &MyWindow::timeout ), 1000 );
swapButton.signal_clicked().connect(
sigc::mem_fun( *this, &MyWindow::toggleUseOfFrame ) );
}
// Periodically update the label to force it to redraw.
bool MyWindow::timeout()
{
Glib::DateTime now = Glib::DateTime::create_now_local();
timeLabel.set_text( now.format( "%S" ) );
return true;
}
// If the frame is currently in use remove it. Otherwise add it back.
void MyWindow::toggleUseOfFrame()
{
if( frame.get_parent() ) {
remove();
box.reparent( *this );
}
else {
box.reparent( frame );
add( frame );
}
}
int main( int argc, char* argv[]) {
Glib::RefPtr<Gtk::Application> app =
Gtk::Application::create( argc, argv, "test" );
MyWindow myWindow;
return app->run( myWindow );
}
Does anyone know if we can plot an area chart using the Qwt library on Qt creator ? If yes, could someone help me with a sample program ?
Based on the screenshots and the listing of examples, I would look at the source for cpuplot.
./qwt-6.1.2/examples>ls
animation curvdemo1 examples.pro radio simpleplot sysinfo
barchart dials friedberg rasterview sinusplot tvplot
bode distrowatch itemeditor realtime spectrogram
controls event_filter legends refreshtest stockchart
cpuplot examples.pri oscilloscope scatterplot stylesheets
./qwt-6.1.2/examples/cpuplot>ls
cpupiemarker.cpp cpuplot.cpp cpuplot.pro cpustat.h
cpupiemarker.h cpuplot.h cpustat.cpp
And
cpuplot.cpp
#include <qapplication.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qpainter.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_curve.h>
#include <qwt_scale_draw.h>
#include <qwt_scale_widget.h>
#include <qwt_legend.h>
#include <qwt_legend_label.h>
#include <qwt_plot_canvas.h>
#include "cpupiemarker.h"
#include "cpuplot.h"
class TimeScaleDraw: public QwtScaleDraw
{
public:
TimeScaleDraw( const QTime &base ):
baseTime( base )
{
}
virtual QwtText label( double v ) const
{
QTime upTime = baseTime.addSecs( static_cast<int>( v ) );
return upTime.toString();
}
private:
QTime baseTime;
};
class Background: public QwtPlotItem
{
public:
Background()
{
setZ( 0.0 );
}
virtual int rtti() const
{
return QwtPlotItem::Rtti_PlotUserItem;
}
virtual void draw( QPainter *painter,
const QwtScaleMap &, const QwtScaleMap &yMap,
const QRectF &canvasRect ) const
{
QColor c( Qt::white );
QRectF r = canvasRect;
for ( int i = 100; i > 0; i -= 10 )
{
r.setBottom( yMap.transform( i - 10 ) );
r.setTop( yMap.transform( i ) );
painter->fillRect( r, c );
c = c.dark( 110 );
}
}
};
class CpuCurve: public QwtPlotCurve
{
public:
CpuCurve( const QString &title ):
QwtPlotCurve( title )
{
setRenderHint( QwtPlotItem::RenderAntialiased );
}
void setColor( const QColor &color )
{
QColor c = color;
c.setAlpha( 150 );
setPen( QPen( Qt::NoPen ) );
setBrush( c );
}
};
CpuPlot::CpuPlot( QWidget *parent ):
QwtPlot( parent ),
dataCount( 0 )
{
setAutoReplot( false );
QwtPlotCanvas *canvas = new QwtPlotCanvas();
canvas->setBorderRadius( 10 );
setCanvas( canvas );
plotLayout()->setAlignCanvasToScales( true );
QwtLegend *legend = new QwtLegend;
legend->setDefaultItemMode( QwtLegendData::Checkable );
insertLegend( legend, QwtPlot::RightLegend );
setAxisTitle( QwtPlot::xBottom, " System Uptime [h:m:s]" );
setAxisScaleDraw( QwtPlot::xBottom,
new TimeScaleDraw( cpuStat.upTime() ) );
setAxisScale( QwtPlot::xBottom, 0, HISTORY );
setAxisLabelRotation( QwtPlot::xBottom, -50.0 );
setAxisLabelAlignment( QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom );
/*
In situations, when there is a label at the most right position of the
scale, additional space is needed to display the overlapping part
of the label would be taken by reducing the width of scale and canvas.
To avoid this "jumping canvas" effect, we add a permanent margin.
We don't need to do the same for the left border, because there
is enough space for the overlapping label below the left scale.
*/
QwtScaleWidget *scaleWidget = axisWidget( QwtPlot::xBottom );
const int fmh = QFontMetrics( scaleWidget->font() ).height();
scaleWidget->setMinBorderDist( 0, fmh / 2 );
setAxisTitle( QwtPlot::yLeft, "Cpu Usage [%]" );
setAxisScale( QwtPlot::yLeft, 0, 100 );
Background *bg = new Background();
bg->attach( this );
CpuPieMarker *pie = new CpuPieMarker();
pie->attach( this );
CpuCurve *curve;
curve = new CpuCurve( "System" );
curve->setColor( Qt::red );
curve->attach( this );
data[System].curve = curve;
curve = new CpuCurve( "User" );
curve->setColor( Qt::blue );
curve->setZ( curve->z() - 1 );
curve->attach( this );
data[User].curve = curve;
curve = new CpuCurve( "Total" );
curve->setColor( Qt::black );
curve->setZ( curve->z() - 2 );
curve->attach( this );
data[Total].curve = curve;
curve = new CpuCurve( "Idle" );
curve->setColor( Qt::darkCyan );
curve->setZ( curve->z() - 3 );
curve->attach( this );
data[Idle].curve = curve;
showCurve( data[System].curve, true );
showCurve( data[User].curve, true );
showCurve( data[Total].curve, false );
showCurve( data[Idle].curve, false );
for ( int i = 0; i < HISTORY; i++ )
timeData[HISTORY - 1 - i] = i;
( void )startTimer( 1000 ); // 1 second
connect( legend, SIGNAL( checked( const QVariant &, bool, int ) ),
SLOT( legendChecked( const QVariant &, bool ) ) );
}
void CpuPlot::timerEvent( QTimerEvent * )
{
for ( int i = dataCount; i > 0; i-- )
{
for ( int c = 0; c < NCpuData; c++ )
{
if ( i < HISTORY )
data[c].data[i] = data[c].data[i-1];
}
}
cpuStat.statistic( data[User].data[0], data[System].data[0] );
data[Total].data[0] = data[User].data[0] + data[System].data[0];
data[Idle].data[0] = 100.0 - data[Total].data[0];
if ( dataCount < HISTORY )
dataCount++;
for ( int j = 0; j < HISTORY; j++ )
timeData[j]++;
setAxisScale( QwtPlot::xBottom,
timeData[HISTORY - 1], timeData[0] );
for ( int c = 0; c < NCpuData; c++ )
{
data[c].curve->setRawSamples(
timeData, data[c].data, dataCount );
}
replot();
}
void CpuPlot::legendChecked( const QVariant &itemInfo, bool on )
{
QwtPlotItem *plotItem = infoToItem( itemInfo );
if ( plotItem )
showCurve( plotItem, on );
}
void CpuPlot::showCurve( QwtPlotItem *item, bool on )
{
item->setVisible( on );
QwtLegend *lgd = qobject_cast<QwtLegend *>( legend() );
QList<QWidget *> legendWidgets =
lgd->legendWidgets( itemToInfo( item ) );
if ( legendWidgets.size() == 1 )
{
QwtLegendLabel *legendLabel =
qobject_cast<QwtLegendLabel *>( legendWidgets[0] );
if ( legendLabel )
legendLabel->setChecked( on );
}
replot();
}
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QWidget vBox;
vBox.setWindowTitle( "Cpu Plot" );
CpuPlot *plot = new CpuPlot( &vBox );
plot->setTitle( "History" );
const int margin = 5;
plot->setContentsMargins( margin, margin, margin, margin );
QString info( "Press the legend to en/disable a curve" );
QLabel *label = new QLabel( info, &vBox );
QVBoxLayout *layout = new QVBoxLayout( &vBox );
layout->addWidget( plot );
layout->addWidget( label );
vBox.resize( 600, 400 );
vBox.show();
return a.exec();
}
Hope that helps.
I have modified the osgViewerQt example in order to load a point cloud and visualize it in a Qt application. As you can see in the attached image, the cloud point cloud is shown but there is an extra border in the window (see the arrows).
I spent all the weekend trying to figure how to "expand" the window in order to remove that border, but it keeps showing.
Do you know what can I do to remove it? I'll post the code for the modified osgViewerQt and the piece of code where I use it.
viewer_widget.h
#ifndef VIEWER_WIDGET_H
#define VIEWER_WIDGET_H
#include "osgViewer/CompositeViewer"
#include <QTimer>
#include <QWidget>
class QGridLayout;
class QWidget;
class ViewerWidget : public QWidget, public osgViewer::CompositeViewer {
private:
std::string cloud_file;
std::string cloud_filepath;
QTimer timer_;
QWidget* widget;
QGridLayout* grid;
osg::ref_ptr<osgViewer::View> view;
private:
ViewerWidget(const ViewerWidget& V);
ViewerWidget& operator=(const ViewerWidget& V);
private:
QWidget* AddViewWidget(osg::Camera* camera,osg::Node* scene);
osg::Camera* CreateCamera(int x,int y,int w,int h,const std::string& name="",
bool windowDecoration=false
);
osg::Node* ReadOctree(const std::string& file);
public:
ViewerWidget(const std::string& filename,const std::string& filepath,bool color,
osgViewer::ViewerBase::ThreadingModel threadingModel
= osgViewer::CompositeViewer::ThreadPerCamera
);
virtual ~ViewerWidget(void){}
void AddCloud(void);
void StartFrameTimer(int msec=10) { timer_.start(msec); }
virtual void paintEvent( QPaintEvent* event ) { frame(); }
};
#endif // VIEWER_WIDGET_H
osg_viewer.cpp
#include "viewer_widget.h"
#include "osgDB/ReadFile"
#include "osgGA/TrackballManipulator"
#include "osgQt/GraphicsWindowQt"
#include "osgViewer/ViewerEventHandlers"
#include <QGridLayout>
#include <QDebug>
ViewerWidget::ViewerWidget(const std::string &filename,const std::string &filepath,
bool color, osgViewer::ViewerBase::ThreadingModel threadingModel
) :
QWidget(),
cloud_file( filename ),
cloud_filepath( filepath )
{
// this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
connect( &(this->timer_), SIGNAL(timeout()), this, SLOT(update()) );
}
QWidget* ViewerWidget::AddViewWidget(osg::Camera *camera,osg::Node *scene) {
view = new osgViewer::View;
view->setCamera( camera );
view->setSceneData( scene );
osg::Stats* stats = this->getViewerStats();
if(stats) stats->report(std::cout);
addView( view );
view->addEventHandler( new osgViewer::StatsHandler );
view->setCameraManipulator( new osgGA::TrackballManipulator );
osgQt::GraphicsWindowQt* gw = dynamic_cast<osgQt::GraphicsWindowQt*>(
camera->getGraphicsContext()
);
return gw ? gw->getGLWidget() : 0;
}
osg::Camera* ViewerWidget::CreateCamera(int x,int y,int w,int h,const std::string &name,
bool windowDecoration
) {
osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->windowName = name;
traits->windowDecoration = windowDecoration;
traits->x = x;
traits->y = y;
qDebug() << "w:" << w << " h:" << h;
traits->width = w;
traits->height = h;
traits->doubleBuffer = true;
traits->alpha = ds->getMinimumNumAlphaBits();
traits->stencil = ds->getMinimumNumStencilBits();
traits->sampleBuffers = ds->getMultiSamples();
traits->samples = ds->getNumMultiSamples();
osg::ref_ptr<osg::Camera> camera = new osg::Camera;
camera->setGraphicsContext( new osgQt::GraphicsWindowQt(traits.get()) );
camera->setClearColor( osg::Vec4(0,0,0,1) );
camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
camera->setProjectionMatrixAsPerspective(
30.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 1.0f, 10000.0f );
return camera.release();
}
osg::Node* ViewerWidget::ReadOctree(const std::string &file) {
osg::Group* group = new osg::Group;
group->addChild( osgDB::readNodeFile(file, options) );
return group;
}
void ViewerWidget::AddCloud() {
std::cout << "Loading cloud from file:" << cloud_file.c_str() << "\n";
QWidget* widget = AddViewWidget(
CreateCamera(0,0,100,100,"cam1",true),
ReadOctree(cloud_file)
);
grid = new QGridLayout;
grid->addWidget( widget, 0, 0 );
this->setLayout( grid );
}
Now, in where this widget is used (simplified a bit to show only the relevant parts):
cloud.h
#ifndef CLOUD_H
class Cloud: public QObject {
Q_OBJECT
private:
osg::ref_ptr<ViewerWidget> osg_widget;
QDockWidget* dock;
/// MORE ATTRIBUTES
public:
Cloud(){
/// ...
dock = new QDockWidget;
osg_widget = new ViewerWidget( getFileName(), getFilePath(), has_color);
dockWidget->setAllowedAreas(Qt::RightDockWidgetArea);
dockWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
dockWidget->addWidget(osg_widget);
}
/// More methods, cloud manipulators, etc.
};
#endif
When specifying the size policy, I have also tried with Minimum, MinimumExpanding and
Ignored, but with the same effect. I tried to specify the size policy directly inside the ViewerWidget (as it inherits from QWidget) and to specify it its widget attribute, too, but with no success.
You're using a grid layout to insert the view widget in your target window, if I understand correctly:
grid = new QGridLayout;
grid->addWidget( widget, 0, 0 );
this->setLayout( grid );
Layouts usually insert padding around their elements (called margin in the Qt docs). You can tune that using QLayout::setContentsMargin(), so here something in the spirit of this
grid->setContentsMargins(0,0,0,0);
should do the trick.
I am using the Mobile Devlopment API called Mosync to develop a Combobox(subclassed from Label) in Mosync, but it seems that when I run my app, the combobox label is shown but its contents (a listbox & button) are not shown.
What do you tink is happening?
class ComboBox : Label
{
private:
Vector <String> options;
ComboBoxButton *button;
MAUI::ListBox *optionsBox;
public:
ComboBox( Vector <String> nOptions, MAUI::Widget *nParent )
: Label( 0, 0, 0 0, nParent ),
options(nOptions)
{
// Constructor:
button = new ComboBoxButton( "", this );
optionsBox = new MAUI::ListBox( 0, 0, 0, 0, NULL, MAUI::ListBox::LBO_VERTICAL, MAUI::ListBox::LBA_LINEAR, false );
optionsBox -> setAutoSize( true );
// Add blank element to CB
optionsBox -> add( new MAUI::Label( 0, 0, 0, 0, optionsBox, "", 0, QuickApp::defFont ) );
// Add elements to CB
for ( int i=0; i<nOptions.size(); i++ )
{
optionsBox -> add( new MAUI::Label( 0, 0, 0, 0, optionsBox, nOptions[i], 0, QuickApp::defFont ) );
}
optionsBox -> setHeight( optionsBox->getChildren()[0]->getHeight() );
this -> add( optionsBox );
this -> add( button );
}
ComboBox()
{
// Destructor:
}
void onButtonClick( bool clicked )
{
// Post: Close/Open drop down options/combo box
if ( clicked )
{
// Show all rows in the listbox
int height = 0;
for ( Vector <Widget*> rows = optionsBox->getChildren(); !rows.empty(); rows.remove(0) )
{
height += rows[0]->getHeight();
}
optionsBox -> setHeight( height );
}
else
{
// Only show the 1st row of the listbox
// Note: Ideally I would like to get the selected cell & make its position
// at the top of the LB
// But I cant find a ListBox function that allows me to either remove a
// specific row or to add a Widget at the beginning of the Listbox (not at the end)
optionsBox -> setHeight( optionsBox->getChildren()[0]->getHeight() );
}
}
};
class ComboBoxButton : Button
{
private:
ComboBox *parent;
bool clickStatus;
public: // QAButton inherits from Label & PointerListener (simple interface with virtual PointerPressEvent(), etc. functions)
ComboBoxButton( String nCaption, ComboBox *nParent )
: Button( "", (MAUI::Widget*)nParent ),
parent(nParent), clickStatus(false)
{
// Constructor:
}
void pointerPressEvent( MAPoint2d point )
{
// Post:
Point p;
p.set( point.x, point.y );
if ( this->contains(p) )
{
clickStatus = !clickStatus;
parent -> onButtonClick( clickStatus );
}
}
void pointerMoveEvent( MAPoint2d point )
{
// Post:
}
void pointerReleaseEvent( MAPoint2d point )
{
// Post:
}
};
class Button : public Label, public PointerListener
{
public:
Button( String nCaption, MAUI::Widget *nParent )
: MAUI::Label( 0, 0, 0, 0, nParent, nCaption, 0, QuickApp::defFont )
{
this -> setAutoSizeX();
this -> setAutoSizeY();
this -> setHorizontalAlignment( MAUI::Label::HA_CENTER );
this -> setVerticalAlignment( MAUI::Label::VA_CENTER );
Environment &env = Environment::getEnvironment();
env.addPointerListener( this );
}
protected:
};
Are you doing a ->show() somewhere, I didn't see it in you code.
Hope this helps.
/Tony
PS. There's a new version MoSync 2.6 at:
http://www.mosync.com/documentation/manualpages/whats-new-mosync-26-pyramid