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
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.
How is it possible to change the color and/or text(format) of a QProgressBar if the mouse is over the filled part of the bar?
I have stacked multiple QProgressBars on top of each other, each showing more content than the previous.
I want to highlight the biggest bar still under the mouse on mouseover and show some bar-specific text. However, the bars are the same size and therefore I want to recognize the filled area of the bar.
E.g., if the mouse is over the third bar, I want to highlight the part from the left to the third bar and show text specific to the third bar.
This is the code I use for the stacked ProgressBars:
class MultiProgressBar : public QWidget
{
Q_OBJECT
public:
MultiProgressBar( QWidget* parent = Q_NULLPTR ) : QWidget( parent ), layout( new QStackedLayout( this ) )
{
layout->setMargin( 0 );
layout->setStackingMode( QStackedLayout::StackAll );
}
void insertBar( QColor const& color )
{
auto bar = new QProgressBar();
bar->setTextVisible( false );
bar->setRange( 0, 10000 );
QPalette palette = this->palette();
palette.setColor( QPalette::Highlight, QColor( color.red(), color.green(), color.blue(), 100 ) );
palette.setColor( QPalette::Base, QColor( color.red(), color.green(), color.blue(), 0 ) );
bar->setPalette( palette );
layout->addWidget( bar );
progress_bars.push_back( bar );
}
public slots:
void setValues( const std::vector< int >& values, const std::vector< std::string >& names )
{
if ( values.size() < progress_bars.size() )
{
for ( auto* widget : progress_bars )
{
layout->removeWidget( widget );
}
progress_bars.clear();
}
while ( progress_bars.size() < values.size() )
{
insertBar( QColor( 0x46, 0xA1, 0xD9, 255 ) );
}
for ( auto i = 0; i < progress_bars.size(); ++i )
{
progress_bars[ i ]->setValue( values[ i ] );
// progress_bars[ i ]->setFormat( QString::fromStdString( names[ i ] ) );
}
}
private:
std::vector< QProgressBar* > progress_bars;
QStackedLayout* layout;
};
1) To change color of filled area you can set new color to the palette of progressbar like this:
QPalette newPalette = bar.palette();
newPalette.setColor(QPalette::Highlight, "red"); // setting color to red
bar.setPalette(newPalette);
2.1) Assign text to progressbar you can with void setFormat(const QString &format); function like in your commented string
progress_bars[ i ]->setFormat( QString::fromStdString( names[ i ] ) );
2.2) Get this text you can by calling QProgressBar::text() function;
3) If you want to highlight specific progressbars you can reimplement
QWidget::mouseMoveEvent(QMouseEvent *event);
in which you can calculate margins of filled area:
void mouseMoveEvent(QMouseEvent *e){
highlightProgressBars(e->pos()); // passing position of mouse cursor
}
NOTE: Don't forget to enable mouse tracking for progressbars and your MultiProgressBar widget:
bar->setMouseTracking(true);
4) Function below gets position of mouse in parameter. It highlights area from left to pointed progressbar inclusively and shows in console text assigned to highlighted progressbars through format property
void highlightProgressBars(QPoint point){
int widthOfBar = ((QProgressBar*)progress_bars.at(0))->width();
int valueForPoint = 10000 / widthOfBar; // value of progressbar for width==1
for (auto pb = this->progress_bars.begin(); pb != this->progress_bars.end(); ++pb) { // iterating vector to paint progressbars
int leftMargin = 0; // "left margin" of current progressbar is in fact right margin of filled area of previous progressbar
if(pb != this->progress_bars.begin()){ // except first progressbar in vector which doesn't have previous progressbar
--pb; // get previous progressbar
leftMargin = ((QProgressBar*)*pb)->value() / valueForPoint; // get width of filled area
++pb; // return to current progressbar
}
if(leftMargin < point.x()) { // if position of cursor is to the right of "left margin" of current progressbar we highlight it
QPalette newPal = ((QProgressBar*)*pb)->palette(); //getting palette
newPal.setColor(QPalette::Highlight,"red"); // and setting color of filled area
((QProgressBar*)*pb)->setPalette(newPal); // finally setting palette to widget
qDebug() << ((QProgressBar*)*pb)->text(); // show text of highlighted progressbar in console
}
else{ // if not we highlight it another way
QPalette newPal = ((QProgressBar*)*pb)->palette();
newPal.setColor(QPalette::Highlight,"lightblue");
((QProgressBar*)*pb)->setPalette(newPal);
}
}
}
If you want to highlight only one progressbar you should change in code above this:
if(leftMargin < point.x()) {
to:
int rightMargin = ((QProgressBar*)*pb)->value() / valueForPoint;
if(leftMargin < point.x() && point.x() < rightMargin) {
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 am using wxWidgets 2.8, together with wxPropertyGrid 1.4 in an application.
For float values, I would like to use a slider to edit them. However, a slider editor is not provided by default, so I have resorted to implementing my own editor, following the guidelines mentioned in the documentation.
However, with this new editor, even though I set it as the editor for my float property, it's not actually appearing until the property grid cell is in any way interacted with (e.g. clicked). Until then, the classic, textbox based controller is still visible.
Apparently, the actual CreateControl method for the slider editor isn't called when the propgrid gets generated - it's only called when the cell itself is somehow interacted with.
Here's my custom property editor:
wxpgslider.h
class WXDLLIMPEXP_PG wxPGSliderEditor : public wxPGEditor
{
#ifndef SWIG
WX_PG_DECLARE_EDITOR_CLASS(wxPGSliderEditor)
#endif
public:
wxPGSliderEditor (int p = 10000)
: precision(p)
{
}
~wxPGSliderEditor ()
{}
// Macro for the CreateControls method stub
wxPG_DECLARE_CREATECONTROLS
void UpdateControl ( wxPGProperty* property, wxWindow* wnd) const;
bool OnEvent ( wxPropertyGrid* propgrid, wxPGProperty* property, wxWindow* wnd, wxEvent& event) const;
bool GetValueFromControl ( wxVariant& variant, wxPGProperty* property, wxWindow* wnd) const;
void SetValueToUnspecified ( wxPGProperty* property, wxWindow* wnd) const;
//void DrawValue ( wxDC& dc, const wxRect& rect, wxPGProperty* property, const wxString& text) const;
private:
int precision;
};
wxpgslider.cpp
#include "cseditor/wxpgslider.h"
//----------------- wxPGSliderEditor ---------------------
WX_PG_IMPLEMENT_EDITOR_CLASS(SliderEditor, wxPGSliderEditor, wxPGEditor)
wxPGWindowList wxPGSliderEditor::CreateControls( wxPropertyGrid* propgrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& size ) const
{
double v_d = property->GetValue().GetDouble();
if ( v_d 1 )
v_d = 1;
wxSlider *ctrl = new wxSlider();
#ifdef __WXMSW__
ctrl->Hide();
#endif
ctrl->Create ( propgrid->GetPanel(),
wxPG_SUBID2,
(int)(v_d * precision),
0,
precision,
pos,
size,
wxSL_HORIZONTAL );
return wxPGWindowList(ctrl);
}
void wxPGSliderEditor::UpdateControl ( wxPGProperty* property, wxWindow* wnd ) const
{
wxSlider* ctrl = wxDynamicCast ( wnd, wxSlider );
if ( ctrl )
{
double val;
if (wxPGVariantToDouble (property->DoGetValue(), &val))
{
if ( val 1 )
val = 1;
ctrl->SetValue ( (int)(val * precision) );
//static_cast(property)->GetLabel()
// ->SetValue( wxString::Format(wxT("%ld"), val * precision) );
}
}
}
bool wxPGSliderEditor::OnEvent ( wxPropertyGrid* propgrid,
wxPGProperty* property,
wxWindow* wnd,
wxEvent& event ) const
{
if(event.GetEventType() == wxEVT_SCROLL_CHANGED)
{
// Update the value
event.Skip();
propgrid->EditorsValueWasModified();
return true;
}
return false;
}
bool wxPGSliderEditor::GetValueFromControl ( wxVariant& variant,
wxPGProperty* property,
wxWindow* wnd ) const
{
wxSlider* ctrl = wxDynamicCast ( wnd, wxSlider );
if ( ctrl )
{
variant = wxVariant ( (double)(ctrl->GetValue())/(double)(precision) );
property->SetValue ( variant );
}
return true;
}
void wxPGSliderEditor::SetValueToUnspecified ( wxPGProperty* property, wxWindow* wnd) const
{
wxSlider* ctrl = wxDynamicCast ( wnd, wxSlider );
if ( ctrl )
{
ctrl->SetValue (0);
}
}
And this is the code I'm using to generate the slider, in a Populate function:
double value = variant->GetFloat();
// Generate a homebrewed slider
wxFloatProperty* fp = new wxFloatProperty(translatedName, originalName, value);
wxPGEditor* rHandle = wxPropertyGrid::RegisterEditorClass(new wxPGSliderEditor(), wxT("SliderEditor"));
fp->SetEditor(rHandle);
page->AppendIn(categoryID, fp);
I register the class, in case it's not been registered before, then set the property's editor. Then I add the property to the grid. Why is the slider not showing up until the cell gets interacted with?
Is calling pgMan->GetGrid()->SelectProperty(fp, false); the only way to make it get drawn?
you are using
#ifdef __WXMSW__
ctrl->Hide();
#endif
what's about
#ifdef __WXMSW__
ctrl->Show();
#endif
sample:
....
wxSlider* ctrl = new wxSlider();
#ifdef __WXMSW__
ctrl->Hide();
#endif
....
ctrl->Create ( propgrid->GetPanel(),
wxPG_SUBID2,
(int)(v_d * precision),
0,
precision,
pos,
size,
wxSL_HORIZONTAL );
#ifdef __WXMSW__
ctrl->Show();
#endif
return wxPGWindowList(ctrl);
}
EDIT
I can not see an OnCustomEditorEvent in your code.
with wxEVT_SCROLL_THUMBTRACK or wxEVT_SCROLL_CHANGED.
....
propgrid->Connect( wxPG_SUBID2, wxEVT_SCROLL_CHANGED,
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
&wxPropertyGrid::OnCustomEditorEvent, NULL, propgrid );
#ifdef __WXMSW__
ctrl->Show();
#endif
return wxPGWindowList(ctrl);
}
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.