Marcin B.

Marcin B. Ruby on rails
developer, Software
Engineer in test and
So...

Temat: Własny event przy zmianie wartości dla QSpinBox

Witam.
Napisałem program do sprawdzania losowań dużego lotka.
Liczby są umieszczone w kontrolce QSpinBox.
Po każdej zmianie wartości kontrolka sprawdza czy jej wartość znajduje się na liście wylosowanych liczb.
Moje rozwiązanie jest takie. Mam własny filtr, który podpinam do tych kontrolek:
filter.reset( new FilterModifiedChange( this, numbers, font, newFont, color ) );
form->spinBox_1->installEventFilter( filter.get() );

A samo filtrowanie odbywa się tak:

bool FilterModifiedChange::eventFilter(QObject *obj, QEvent *event)
{
QFocusEvent* fEvent = static_cast< QFocusEvent* > ( event );
if( fEvent && ( fEvent->type() == QEvent::FocusOut ) ) { // obejscie
QSpinBox* spinBox = parent->findChild< QSpinBox* >( obj->objectName() );
changeColorsAndFont( spinBox );

return QObject::eventFilter(obj, event);
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}

}

Uważam jednak, że zastosowanie typu eventu QEvent::FocusOut jest obejściem,
dodatkowo w aplikacji muszę oprogramować każdą zmianę wartości w tej kontrolce, aby kolorować wylosowane liczby (i tak właśnie robie) np. losowe generowanie liczb.
Jednak wydaje mi się to drogą na około. Nie znalazłem jednak eventa odpowiedzialnego za zmianę wartości w danej kontrolce.
Wobec tego moje pytanie brzmi:
czy można zdefiniować taki event, który będzie emitowany za każdym razem, gdy zmieni się wartość w kontrolce QSpinBox bez konieczności tworzenia pochodnej klasy QSpinBox ?
Jeśli tak to jak podpiąć taki event do kontrolki ?
Jakie są alternatywne, ale efektywne i eleganckie rozwiązania tego problemu ?
Wasze sugestie proszę poprzeć fragmentem kodu.

Pozdrawiam :)
Tomasz S.

Tomasz S. Nokia Certified Qt
Specialist

Temat: Własny event przy zmianie wartości dla QSpinBox

Cześć,

myślę że chodzi ci o to:
void valueChanged ( int i )

void valueChanged ( const QString & text )

To są sygnały ze SpinBoxa.
Radzę poczytać o sygnałach i slotach.

pozdrawiam
Tomasz Ziobrowski

Tomasz Ziobrowski Oprogramowanie dla
przemysłu i nie
tylko

Temat: Własny event przy zmianie wartości dla QSpinBox

Marcin B.:
Witam.
Napisałem program do sprawdzania losowań dużego lotka.
Liczby są umieszczone w kontrolce QSpinBox.
Po każdej zmianie wartości kontrolka sprawdza czy jej wartość znajduje się na liście wylosowanych liczb.
Moje rozwiązanie jest takie. Mam własny filtr, który podpinam do tych kontrolek:
filter.reset( new FilterModifiedChange( this, numbers, font, newFont, color ) );
form->spinBox_1->installEventFilter( filter.get() );

A samo filtrowanie odbywa się tak:

bool FilterModifiedChange::eventFilter(QObject *obj, QEvent *event)
{
QFocusEvent* fEvent = static_cast< QFocusEvent* > ( event );
if( fEvent && ( fEvent->type() == QEvent::FocusOut ) ) { // obejscie
QSpinBox* spinBox = parent->findChild< QSpinBox* >( obj->objectName() );
changeColorsAndFont( spinBox );

return QObject::eventFilter(obj, event);
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}

}

Uważam jednak, że zastosowanie typu eventu QEvent::FocusOut jest obejściem,
dodatkowo w aplikacji muszę oprogramować każdą zmianę wartości w tej kontrolce, aby kolorować wylosowane liczby (i tak właśnie robie) np. losowe generowanie liczb.
Jednak wydaje mi się to drogą na około. Nie znalazłem jednak eventa odpowiedzialnego za zmianę wartości w danej kontrolce.
Wobec tego moje pytanie brzmi:
czy można zdefiniować taki event, który będzie emitowany za każdym razem, gdy zmieni się wartość w kontrolce QSpinBox bez konieczności tworzenia pochodnej klasy QSpinBox ?
Jeśli tak to jak podpiąć taki event do kontrolki ?
Jakie są alternatywne, ale efektywne i eleganckie rozwiązania tego problemu ?
Wasze sugestie proszę poprzeć fragmentem kodu.

Pozdrawiam :)
Witaj
Nie do końca rozumiem co chcesz zrobić.
Czy QEvent::FocusOut ma służyć wykryciu momentu w którym kończysz edycję wartości w kontrolce? Jeżeli tak, to faktycznie sygnały i sloty były by o wiele prostszym rozwiązaniem.

Może coś takiego było by prostsze?
 
class Filter: public QObject
{
Q_OBJECT
public:
Filter(QObject * p = NULL):QObject(p) {}

public slots:
void filterValue()
{
QSpinBox * box = qobject_cast<QSpinBox*>(sender());
if (box)
changeColorsAndFont( box );
}
public:
void changeColorsAndFont( QSpinBox *)
{ ... }
};


Natomiast połączenie fitlra ze spinem gdzieś wewnątrz jakiegoś obiektu wyglądało by tak.

Fiter * filter = new Filter(this);
connect(spinBox,SIGNAL(valueChanged(int)),filter,SLOT(filterValue()));


Za każdym razem gdy zmieni się wartość w spinbox'ie filter zostatnie o tym powiadomiony nową wartością ze spinboxa.

Niestety, dzieje się tak zawsze gdy wartość się zmienia. Czyli nie tylko gdy focus opuszcza widget. Nie wiem czy o to Ci chodziło?

Możesz ewentualnie wykorzystać coś takiego.

connect(spinBox,SIGNAL(editingFinisched()),fiter,SLOT(filterValue()));


Wówczas filterValue jest wywołany tylko wtedy gdy widget traci focus lub został naciśnięty enter. Czyli nie ma sprawdzania podczas edytowania wartości spinbox'a.
Ten ostatni przykład powinien robić praktycznie to samo co twój dotychczasowy kod.

Oczywiście możesz podłączyć więcej "spinbox'ów" do jednego filtra za pomocą connect.

Pozdrawiam
TomekTomasz Ziobrowski edytował(a) ten post dnia 21.07.09 o godzinie 00:32
Marcin B.

Marcin B. Ruby on rails
developer, Software
Engineer in test and
So...

Temat: Własny event przy zmianie wartości dla QSpinBox

Już doszedłem do eleganckiego rozwiązania.
Oto co chciałem zrobić:
Na każdy 6 cyfrowy wiersz z numerami dużego lotka (jeden wiersz wymagany, reszta opcjonalna)
sprawdzam czy dana liczba została wylosowana.
Jeśli tak to ustaw odpowiedni kolor i czcionkę, jeśli nie to ustaw domyślny kolor i czcionkę.
I rzeczywiście z sygnałami jest szybciej.
Ale teraz zadanie nr 2.
Dla każdego edytowanego spinboxa w wierszu sprawdź czy w tym wierszu występuje już ta wartość,
jeśli nie to zostaw jak jest, jeśli tak to przywróć poprzednią.

Moje rozwiązanie:
Użyłem dekoratora dla QSpinBox, zapamiętałem udekorowaną kolekcję:
boost::ptr_deque< SpinBoxDecorator > spinDecorators;

Dekorator:
#ifndef SPINBOXDECORATOR_H
#define SPINBOXDECORATOR_H

#include <boost/utility.hpp>
#include <QObject>

class QSpinBox;

class SpinBoxDecorator : public QObject, boost::noncopyable
{
Q_OBJECT;
QSpinBox* mSpinBox;
int mOldValue;
const int mRow;

public:
SpinBoxDecorator( QSpinBox* spinBox, const int row );
~SpinBoxDecorator();

QSpinBox* getSpinBox() { return mSpinBox; }
const QSpinBox* getSpinBox() const { return mSpinBox; }
const int getPreviewValue() const { return mOldValue; }
const int getRow() const { return mRow; }

void setPreviewValue( const int value );

void changeValueWthOutSignal( int value );
signals:
void valueChanged();

private slots:
void serviceValueChanged( int value );
};

#endif // SPINBOXDECORATOR_H

oraz:
#include "spinboxdecorator.h"
#include <QSpinBox>

SpinBoxDecorator::SpinBoxDecorator( QSpinBox* spinBox,
const int row )
: mSpinBox( spinBox ),
mRow( row )
{
mOldValue = mSpinBox->value();
mSpinBox->connect( mSpinBox, SIGNAL(valueChanged(int)),
this, SLOT(serviceValueChanged(int)) );
}

SpinBoxDecorator::~SpinBoxDecorator()
{

}
// Ten sygnał podpiąłem do odpowiedniego slotu,
// i skorzystałem z waszej wskazówki:
// SpinBoxDecorator* spinDecorator = qobject_cast<SpinBoxDecorator*>(sender());
// normalnie wcześniej przekazywałem tutaj wskaźnik przez this
// slot obsługuje zarówno kolorowanie i zmianę czcionki jak
// i zapewnia niepowtarzalność numerów totka w obrębie jednego wiersza
// ( 6 cyfr dużego lotka )
void SpinBoxDecorator::serviceValueChanged( int value )
{
emit valueChanged();
}

void SpinBoxDecorator::changeValueWthOutSignal( int value )
{
mSpinBox->disconnect( mSpinBox, SIGNAL( valueChanged(int) ),
this, SLOT( serviceValueChanged(int) ) );
mSpinBox->setValue( value );
mOldValue = value;
mSpinBox->connect( mSpinBox, SIGNAL(valueChanged(int)),
this, SLOT(serviceValueChanged(int)) );
}

void SpinBoxDecorator::setPreviewValue( const int value )
{
mOldValue = value;
}

Do obsługi koloru i czcionki mam osobny obiekt.
Twoje rozwiązanie jest prostsze. Jednak muszę też zapamiętać gdzieś poprzednią wartość ze spinboxa.
Przy okazji:
QSpinBox * box = qobject_cast<QSpinBox*>(sender());
Bardzo praktyczne widzę. :)
(Gdybym wiedział, że tak można dobrać się do obiektu, który wysłał sygnał to zaimplementowałbym podobne rozwiązanie do twojego ).

Ps. Jeśli jest jeszcze jakieś inne alternatywne elegackie rozwiązanie, to chętnie się z nim zapoznam.
Dziękuję za pomoc i pozdrawiam. :)Marcin B. edytował(a) ten post dnia 22.07.09 o godzinie 10:39

Następna dyskusja:

Jak odczytac plik .ini przy...




Wyślij zaproszenie do