"checked"- status selber bestimmen

Alles rund um die Programmierung mit Qt
Antworten
benicz
Beiträge: 12
Registriert: 24. Juni 2010 17:18

"checked"- status selber bestimmen

Beitrag von benicz »

hallo!

wahrscheinlich einfache, aber anscheinend spezielle frage:
ich möchte eine größere anzahl buttons haben, die "an" oder "aus" sein können. hierzu weise ich per qss im falle von Qmeinbutton::checked ein anderes borderimage zu.
das klappt soweit auch prima!

ihren "checked"-status sollen sie jetzt aber aus einem bitmuster (array von int's) beziehen, damit beim neuzeichnen der gesamten gui alles auf einmal passt

also habe ich von qpushbutton aus eine neue klasse gemacht und eigene isChecked und setChecked -funktionen geschrieben.

anscheinend holt sich die paint-routine den status aber woanders her, denn obwohl ich mal "return false" hartgecodet habe, lassen sich die buttons "toggeln", bzw. wenn ich die bits vorher selber auf "1" setze (muster[0]=0xFF), sind sie trotzdem nicht checked.

.h:

Code: Alles auswählen

class QMeinButton : public QPushButton
{
		Q_OBJECT
		Q_PROPERTY(bool checked READ isChecked WRITE setChecked)

public:
		QMeinButton(char* text=0, int stopnum=0, QWidget *parent=0);
		bool isChecked() const;
		void setChecked(bool);

private:
	int absnum;
};
.cpp:

Code: Alles auswählen

int muster[10];

QMeinButton::QMeinButton(char* text, int absnum, QWidget *parent) :
		QPushButton(text)
{
	this->absnum = absnum;
	setCheckable(true);
}


bool QMeinButton::isChecked() const
{
	int bytenum = absnum >> 5;
	int bitnum = absnum & 0b11111;
	//return IsBit(muster[bytenum], bitnum);
	return false;
}

void QMeinButton::setChecked(bool status)
{
	int bytenum = absnum >> 5;
	int bitnum = absnum & 0b11111;
	if(status)
		sbi(muster[bytenum], bitnum);
	else
		cbi(muster[bytenum], bitnum);
}
die frage also:
wo holt sich paintEvent den wirklichen status her, und wie kann ich den beeinflussen?
oder muss ich etwa ein komplettes paintEvent schreiben? eigentlich funktioniert das ja so, wie es soll...

ich stelle mir das ein bisschen so vor wie bei einer model-basierten tabelle, wenn der vergleich nicht hinkt... ;)

danke schonmal, ich hoffe, ich habe mich klar ausgedrückt :-D
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

*) Ableiten ist hier die falsche Herangehensweise.
*) isChecked()/setChecked() sind NICHT virtuell, das heißt Überschreiben hat keinen Einfluss auf das Verhalten beim Zugriff über Basisklassenzeiger (und das passiert ja im paintEvent von QPushButton - this ist kein QMeinButton*...)
Lösung:
Ganz simpel: Du hast a) deine Buttons, b) dein Bitmuster (QVector<int>?)
-> setButtonStats(const QVector<QPushButton*>& buttons, const QVector<int>& stats);
in dieser freien Funktion (oder von mir aus private Member-Funktion deines verwaltenden Widgets) werden die Buttons nach dem Muster auf einen Schlag gechecked. Fertig :)
benicz
Beiträge: 12
Registriert: 24. Juni 2010 17:18

Beitrag von benicz »

Wow, das klingt einfach, danke!
Das mit den vektoren ist noch neu für mich, da werde ich mich wohl mal einarbeiten ;-)

Schönen Abend wünsch' ich!

EDIT:
so, bin wieder da! ;)
dass ich mich eingearbeitet hätte, ist hoch gegriffen, aber meine bisherigen recherchen deuten alle darauf hin, dass ein qvector einem array sehr ähnlich ist.
und ein weiterer blick auf den lösungsansatz lässt mich vermuten, dass das auf eine funktion rausläuft, die einfach das bitmuster durchsurft und für jeden button setChecked() aufruft.
das ist aber das, was ich eigentlich genau NICHT möchte...

an irgendeiner stelle (im speicher) muss doch der status eines buttons gespeichert sein, und DA würde ich gerne 'ran, das finde ich eleganter ;-)
und vor allem könnte ich die vorhandene onklick- und paint-maschinerie nutzen, daher mein set/isChecked ansatz...
benicz
Beiträge: 12
Registriert: 24. Juni 2010 17:18

Beitrag von benicz »

hmm, ich wollte platz sparen...
damit aber auch bekannt wird, dass ich hier gepostet habe, mach' ich mal nen blindpost ;-) kann ja dann hinterher weg...
Nvidia
Beiträge: 238
Registriert: 22. Februar 2010 21:23

Beitrag von Nvidia »

erklaer nochmal genau was du willst,
ich check das noch nicht.
Du willst den Check-status anhand eines bitmusters bestimmen.
und abhaendig von dem checkstatus des jeweiligen buttons, den malen?
Meinst du das so?
benicz
Beiträge: 12
Registriert: 24. Juni 2010 17:18

Beitrag von benicz »

ok, gerne ;-)

ich möchte, dass der isChecked-status unmittelbar aus einem BIT in meinem muster-array folgt.
wenn sich also irgendwo im programm das bitmuster ändert, reicht ein neuzeichnen der anwendung, und alles wäre supi.

deshalb habe ich auch versucht, die isChecked() zu überschreiben, aber anscheinend greift das paintevent nicht darauf zu, weil es ja denkt, es handele sich um einen QPushButton (wenn ich das richtig verstanden habe).

mir würde also eigentlich der originale quelltext der paint-routine reichen, denn da müsste ja drinstehen, wo er nachschaut, welchen status er hat.

genaugesagt ist mir sogar egal, ob der button selbst gechecked ist, hauptsache, er wird richtig dargestellt.
da ich das ganze gui-design aber bisher per stylesheet mache (super sache!), kann ich nur auf die checked-eigenschaft reagieren...

ein zufuß-alle-durchackern ist für mich erstmal die "triviale lösung".
müsste doch eigentlich gehen, dafür sind wir doch objekt-orientiert, oder?! ;-)
muss ich vielleicht anstelle von QPushButton von QAbstractButton ableiten? das mag er im moment noch nicht (cannot allocate...of abstract type)
Nvidia
Beiträge: 238
Registriert: 22. Februar 2010 21:23

Beitrag von Nvidia »

hm naja das ist doch nicht schwer.
Du koenntest z.b. ein Signal einfuehren und jedes mal, wenn es sich das Bitmuster aendert das Signal ausstrahlen.
Dieses Signal verbindet du dann mit dem slot repaint();
und wenn du auf das Neuzeichnen zugreifen willst.
das kannst du doch ueber das PaintEvent, welches du reimplementierst und dann kannst du doch dort alles mit dem treiben, was du willst.
if(Bitmuster==blalba)
zeiche hieer
else
zeichne dort

ich hoffe das ist was du meinst, bin mir aber nicht sicher ;-)
benicz
Beiträge: 12
Registriert: 24. Juni 2010 17:18

Beitrag von benicz »

im grunde ist es das. aber:
die zeichnerei klappt ja einwandfrei (zu zeichnendes bild wird per stylesheet entschieden)!
was ich eigentlich ändern muss ist die "herkunft" der information, ob der button gecheckt ist, oder nicht. wenn ich das beim debuggen durchtrace, kommt er ja auch brav in die "väterliche" isChecked() rein.

an's paintevent möchte ich deshalb nicht, weil dann der stylesheet-mechanismus nicht mehr funktioniert (oder mir wieder alles kaputtmacht, keine ahnung, ob der overridet)

weiß nicht, wie ich das noch ausdrücken soll... :oops:
TheBert
Beiträge: 21
Registriert: 1. September 2009 14:27

Beitrag von TheBert »

Ich begreife nicht was du möchtest?

Du hast ein bitmuster und ein paar buttons, wenn sich das bitmuster ändert, dann sollen die buttons gechecked sein?

Was willst du dann mit dem paintevent und dem source davon?

Lies dir in der qt doku das signal-slot-konzept durch, das macht genau das was du willst!
benicz
Beiträge: 12
Registriert: 24. Juni 2010 17:18

Beitrag von benicz »

das neuzeichnen würde ich per signal auslösen, wenn sich das bitmuster geändert hat, das ist klar...

ich suche einen weg, durch geschicktes vererben (? :-D ) die "checked"-eigenschaft des buttons von meinem globalen bitmuster abhängig zu machen, und nicht von einer objekteigenen bool-variable.

so besser? :D
padreigh
Beiträge: 340
Registriert: 13. Mai 2010 10:06

Beitrag von padreigh »

Object-Orientiert === Das Object (Der QPushButton) weiss seinen Zustand (private member) und legt ihn anderen per getter/setter offen.
isChecked-status [folgt] unmittelbar aus einem BIT in meinem muster-array folgt.
wenn sich also irgendwo im programm das bitmuster ändert, reicht ein neuzeichnen der anwendung, und alles wäre supi.
Kapsel die Änderung des Bitmusters in einen SLOT der
a) das Bitmuster ändert
b) deine Buttons korrekt setzt .. in etwa so:

Code: Alles auswählen

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtCore>
#include <QtGui>

class BitButtonArray : public QWidget
{
    Q_OBJECT
public:
    explicit BitButtonArray(const QBitArray & init_status, QWidget * parent = 0) :
        QWidget(parent),
        size(init_status.size())
    {
        Q_ASSERT(size > 0 && size < 30);

        QGridLayout * glay = new QGridLayout;
        bg = new QButtonGroup(this);
        bg->setExclusive(false);

        QCheckBox * cb;
        for(int i(0); i < size; ++i)
        {
            cb = new QCheckBox(QString::number(i),this);
            cb->setChecked(init_status.at(i));
            glay->addWidget(cb,i/5,i%5);
            bg->addButton(cb,i);
        }

        setLayout(glay);

        connect(bg,SIGNAL(buttonClicked(int)),this,SLOT(bitMusterChanged()));
    }

    ~BitButtonArray()
    {
    }

public slots:
    void setButtons(const QBitArray & newStatus)
    {
        Q_ASSERT( newStatus.size() >= size);

        bg->disconnect(this,SLOT(bitMusterChanged()));
        for(int i(0); i < size; ++i)
        {
            bg->button(i)->setChecked(newStatus.testBit(i));
        }
        connect(bg,SIGNAL(buttonClicked(int)),this,SLOT(bitMusterChanged()));
        bitMusterChanged();
    }


    void bitMusterChanged()
    {
        QString bitmuster;

        for(int i(0); i < size; ++i)
        {
            if (bg->button(i)->isChecked()) bitmuster.append("1"); else bitmuster.append("0");
        }

        QMessageBox::information(this, "Bitmuster geändert:",bitmuster);
    }

private:
    const int size;
    QButtonGroup * bg;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0) : QMainWindow(parent)
    {
        QBitArray arr (25,true);
        arr.clearBit(1);
        arr.clearBit(3);
        arr.clearBit(5);

        QVBoxLayout * lay = new QVBoxLayout;

        bba = new BitButtonArray(arr);
        lay->addWidget(bba);

        pb =  new QPushButton("Zufallsmuster");
        connect(pb,SIGNAL(clicked()),this,SLOT(zufallsmuster()));

        lay->addWidget(pb);
        QWidget * b = new QWidget;
        b->setLayout(lay);

        setCentralWidget(b);
    }


    ~MainWindow();

public slots:
    void zufallsmuster()
    {

        QBitArray arr(25);
        for (int i(0); i< 25; ++i)
            arr.setBit(i,rand()%2);

        bba->setButtons(arr);
    }
private:
    BitButtonArray * bba;
    QPushButton * pb ;
};

#endif // MAINWINDOW_H

Code: Alles auswählen

#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
Statt checkboxen kannst du dann ja deine css-gestylten qpushbuttons mit setChecked() methodenimplementation nehmen ...
Dateianhänge
vor/nach klick
vor/nach klick
bitmust.png (22.76 KiB) 5497 mal betrachtet
Zuletzt geändert von padreigh am 27. August 2010 14:10, insgesamt 2-mal geändert.
Patrick (QtCreator 1.3.1, Qt 4.6.3)
---
template = subdirs
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

In deinem Lösungswunsch hast du einen riesigen Fehler: Es gibt in C++ keine Möglichkeit, die Veränderung einer Variablen/Speicherbereich/... zu überwachen, also z.B. einen Callback zu installieren, der ausgeführt wird, wenn sich an Stelle XYZ das Bit ändert.
Du kannst aber sehr wohl eine Klasse erstellen, der du Methoden zum Setzen und Lesen einzelner Bits spendierst und die dann entsprechende Nachrichten versendet. Das können jetzt Qt-SIGNALS sein, oder einfache C-Callbacks, oder was mit boost::signals, oder ...
Mit geschickter Operatorüberladung kannst du sogar erreichen, dass sich das Setzen von einzelnen Bits anfühlt, als hättest du gar keine eigene Klasse in der Hand.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

@benicz

Glaub ich vertseh Dein Problem ned ganz.

Ich fass mal zusammen, was ich verstanden hab:
- Du willst QPushButton Objecte verwenden, weil du willst die Stylesheet-Funktionalitaet verwenden, und du willst sicher das layouten von QT uebernehmen
- Dich stoert das Handling mit den bool werten als Basis fuer die Anzeige des Stati (welcher aber von natur aus schon boolean ist oder ?)
- du bekommst deine Infos aus einem Bitfeld, oder einer Variable irgendeines Types, die merhere Zustaende beinhaltet. Der einfachheithalber nehmen wir mal an es ist ein stinknormales Int (32 bit unter nem 32bit System).


Richtig soweit ?
benicz
Beiträge: 12
Registriert: 24. Juni 2010 17:18

Beitrag von benicz »

@padreigh:
wow, danke für die mühen!
im grunde habe ich genau sowas vor.
allerdings nutzt du ja letztenendes auch für jeden button (hier checkbox) einmal setchecked.

und genau das will ich umgehen :-D
vielleicht ist das ja ein hinweis auf "was ich möchte"

dass ich da ohne ereignisse nicht drum herum komme, ist mir klar.
ich weiß ja grundsätzlich, wann sich bits ändern, dann würde ich ein repaint auslösen.

im grunde könnte ich wahrscheinlich einfach hingehen und die klasse QPushButton komplett kopieren. Sie funktioniert ja eigentlilch so, wie ich mir das vorstelle. Nur müsste "mein" button in der "isChecked" nicht bei einem eigenen member schauen, ob "checked" ist, sondern im globalen bitmuster.

ich fand an der OO immer einen vorteil, dass man fertige klassen erben und bestimmte eigenschaften/funktionen ändern (überschreiben) kann. wahrscheinlich habe ich jetzt einfach mal pech, dass sich die ischecked nicht überschreiben lässt? :cry:
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Also: Überschreiben ist kein Problem, nur wird das nur dann korrekt erkannt, wenn
1) die jeweilige Methode virtual ist oder
2) zur Compiletime der Typ mit dem gewollten übereinstimmt.
in QPushButton::paintEvent() ist 2 nicht erfüllt.
Und wie gesagt sind setChecked()/isChecked() nicht virtual.

Aber Ableiten ist hier definitiv nicht notwendig, es macht die Sache nur komplizierter. Und wenn du jetzt tatsächlich die ganze Klasse kopieren willst, nur um deine kleine Bitmuster-Logik einzubauen - bitte tus nicht...

Du wirst doch irgendeine Klasse haben, die das Bitmuster kontrolliert, und eine Klasse die die Buttons kontrolliert (das parent-Widget). Lass doch die beiden miteinander kommunizieren, und in der Klasse die die Buttons kontrolliert in einem SLOT passend zu dem empfangenen SIGNAL das checked-Property des entsprechenden Buttons setzen.
Antworten