Qt - Jak prawidłowo przesłać adres innego obiektu do innego obiektu ?

0

Chcę przesłać adres obiektu do innego obiektu - wiem, że to podstawy ale mam z tym problem, bo niby rozumiem ale jeżeli przychodzi co do czego, to okazuje się, że robię błąd i nie wiem gdzie on jest.
Otóż mam taką klasę

class AccessToDB : public QSqlDatabase
{

public:
    AccessToDB();
    ~AccessToDB();

    bool connectDB();
    QSqlDatabase &addressDB();

private:
    QSqlDatabase db;
    QSqlError errorConnectDB;
};

definicje tej klasy są takie

AccessToDB::AccessToDB()
{
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setHostName("127.0.0.1");
    db.setDatabaseName("dbSpIT.db");
    db.setUserName("");
    db.setPassword("");
}

bool AccessToDB::connectDB()
{
    if(db.open()==true){
        qDebug()<< "Database is open" << db.driver() << db.driverName() << db.isOpen();

        return true;
    }
    else{
        errorConnectDB = db.lastError();
        qDebug()<< "Database not open" << errorConnectDB.text();

        return false;
    }
}

QSqlDatabase &AccessToDB::addressDB()
{
    qDebug()<<"address db is"<< &db;
    return db;
}

AccessToDB::~AccessToDB()
{
    db.close();
}

w funkcji głównej MainWindow w ten oto sposób wywołuję tą funkcję

accessDB = new AccessToDB();
accessDB->connectDB();

do tej pory jest OK ale mam potrzebę przesłać ten adres dalej do zupełnie innej klasy, która wygląda tak

BorrowingUsersWindow::BorrowingUsersWindow(QWidget *parent) : QDialog(parent)
{
    this->resize(400,300);
    this->setWindowTitle(QString(tr("Dodawanie użytkownika wypożyczającego")));

    mainLayout = new QVBoxLayout(this);

    lblNameSurnameHLayout = new QHBoxLayout();

    lblName = new QLabel(this);
    lblName->setText(QString(tr("Imię")));

    lblSurname = new QLabel(this);
    lblSurname->setText(QString(tr("Nazwisko")));

    leEdtNameSurnameHLayout = new QHBoxLayout();

    leEdtName = new QLineEdit(this);
    leEdtSurname = new QLineEdit(this);

    modelSqlProducer = new SqlQueryModelProducer(this, dbConn);

    tblViewUserData = new QTableView(this);
    tblViewUserData->setModel(modelSqlProducer);

    bttHLayout = new QHBoxLayout();

    bttAdd = new QPushButton(this);
    bttAdd->setText(QString(tr("Dodaj")));

    bttQuit = new QPushButton(this);
    bttQuit->setText(QString(tr("Zamknij")));

    bttRem = new QPushButton(this);
    bttRem->setText(QString(tr("Usuń")));

    lblNameSurnameHLayout->addWidget(lblName);
    lblNameSurnameHLayout->addWidget(lblSurname);

    leEdtNameSurnameHLayout->addWidget(leEdtName);
    leEdtNameSurnameHLayout->addWidget(leEdtSurname);

    bttHLayout->addWidget(bttAdd);
    bttHLayout->addWidget(bttRem);
    bttHLayout->addWidget(bttQuit);

    mainLayout->addLayout(lblNameSurnameHLayout);
    mainLayout->addLayout(leEdtNameSurnameHLayout);
    mainLayout->addWidget(tblViewUserData);
    mainLayout->addLayout(bttHLayout);

    QObject::connect(bttQuit, &QPushButton::clicked, this, &QDialog::close);
}

QSqlDatabase *BorrowingUsersWindow::setConnect(QSqlDatabase &connect)
{
    qDebug()<<"adres db"<< &connect; //tutaj chcę odebrać ten adres
    dbConn = &connect;
    return dbConn;
}

pytanie jest takie, jak prawidłowo przesłać adres utworzonego obiektu accessDB = new AccessToDB(); do zupełnie innego obiektu QSqlDatabase *BorrowingUsersWindow::setConnect(QSqlDatabase &connect) bo chcę w poniższym modelu wyświetlić tabelę

Model SQL

SqlQueryModelProducer::SqlQueryModelProducer(QObject *parent, QSqlDatabase *dbConnect) : QSqlQueryModel{parent}
{
    qDebug()<< "addr" << dbConnect;
    QSqlDatabase db = QSqlDatabase::addDatabase(dbConnect->driver(), dbConnect->connectionName()); //tutaj mam segmentation fault

//    QString userData = "SELECT pIDuserWyp, pImie, pNazwisko FROM tUzytkownicyWypozyczajacy";
//    queryUserData = new QSqlQuery(userData, db);

//    if(queryUserData->exec()==true){
//        qDebug()<< "zapytanie wykonane";
//        while(queryUserData->next()){
//            QSqlRecord rec = queryUserData->record();
//            QVariant var = rec.value(0);
//            qDebug()<< var.toString();
//        }
//    }
//    else{
//        qDebug()<< "coś poszło nie tak z wykonaniem zapytania";
//    }
}

PS. Być może pytanie zadaję źle no ale póki co chciałbym zobaczyć co odpowiecie, bo może dobrze zobrazowałem problem, jeżeli nie, to przemyślę jeszcze raz zadanie pytania

0

Wybacz, nie chce mi się wczytywać o północy. W skrócie: pomysł wydaje się nieczysty

ALE

W języku C++, przeciwnie niż w Java C#, przekazywanie wskaźników / referencji moze być groźne, konkretnie można po jakiejś sekwencji działań posiadać wskaźnik / referencję, która wskazuje w kosmos.

1
class SingleAccess
{
    public:
    //static AccessToDB *db() // wersja 1
    AccessToDB *operator->()const // wersja 2
    {
        static AccessToDB *accessDB= new AccessToDB();
        static bool connected=false;
        if(!connected)
        {
            accessDB->connectDB();
            connected=true;
        }
        return accessDB;
    }
};

W dowolnym miejscu:
SingleAccess::db()->addressDB(); // wersja 1
SingleAccess()->addressDB(); // wersja 2

3

Rada numer jeden - zapomnij o wskaźnikach ;)

QSqlDatabase *BorrowingUsersWindow::setConnect(QSqlDatabase &connect)

Ta funkcja oczekuje referencji, zatem

AccessToDB accessDB;
accessDB.connectDB();
// ...
whatever.setConnect(accessDB);
0

@Bartłomiej Golenko:

Rada numer jeden - zapomnij o wskaźnikach

więc jak sobie radzić w podobnych sytuacjach ?

0

Pozbyłbym się zupełnie klasy AccessToDb, bo ona niczego nie wnosi, tym bardziej, że później kombinujesz jak wystawić niby prywatne pole(QSqlDatabase) na zewnątrz klasy.
Do łączenia z bazą zrobiłbym zwyczajną funkcję, coś w stylu
QSqlError connectToDb(DbConnectionParams const& Params).

Model który ma korzystać z bazy dostałby konstruktor
SqlQueryModelProducer::SqlQueryModelProducer(QString const& connectionName, QObject* parent = nullptr).
I sam by się dobierał do odpowiedniej instancji QSqlDatabase.

BTW nazwa SqlQueryModelProducer jest myląca.

0
zkubinski w komentarzu napisał(a):

właśnie już miałem pisać o poradę, bo tak naprawdę @_13th_Dragon dał świetne rozwiązanie tylko problem w tym, że ja to połączenie tworzę w MainWindow, a chyba o tym nie wspominałem, a robienie drugiej instancji MainWindow w oknie podrzędnym po to aby dorwać się do adresu połączenia jest bez sensu i właśnie miałem pytać o pomysły jak to obejść - przemyślę Twoją propozycję.

Więc totalnie nie zrozumiałeś podanego rozwiązania.
W poniższym przykładzie połączenie tworzone w foo() następnie dokładnie to samo połączenie użyto w bar() i w main()

#include <iostream>
using namespace std;

class AccessToDB
{
	public:
	AccessToDB() { cout<<"constructor: "<<this<<endl; }
	void addressDB() { cout<<"addressDB: "<<this<<endl; }
	~AccessToDB() { cout<<"destructor: "<<this<<endl; }
};

class SingleAccess
{
    public:
    static AccessToDB &db()
    {
        static AccessToDB access();
        return access;
	}
    AccessToDB *operator->()const
    {
    	return &(db());
    }
};

void bar()
{
	cout<<"bar start"<<endl;
	SingleAccess::db().addressDB();
	SingleAccess()->addressDB();
	cout<<"bar done"<<endl;
}

void foo()
{
	cout<<"foo start"<<endl;
	SingleAccess::db().addressDB();
	SingleAccess()->addressDB();
	bar();
	cout<<"foo done"<<endl;
}

int main()
{
	cout<<"main start"<<endl;
	foo();
	SingleAccess::db().addressDB();
	SingleAccess()->addressDB();	
	cout<<"main done"<<endl;
	return 0;
}
0
_13th_Dragon napisał(a):

Więc totalnie nie zrozumiałeś podanego rozwiązania.

Koledze jest trudno pomóc ...
cóz, niektórzy nie tylko nie chwycą koła ratunkowego, ale ono może ich zabic

3
zkubinski napisał(a):

Otóż mam taką klasę

class AccessToDB : public QSqlDatabase
{

public:
    AccessToDB();
    ~AccessToDB();

    bool connectDB();
    QSqlDatabase &addressDB();

private:
    QSqlDatabase db;
    QSqlError errorConnectDB;
};

Po kiego grzyba to dziedzicznie, skoro masz pole w tej klasie tego samego typu i na dodatek korzystasz z tego pola.

3
zkubinski napisał(a):

do tej pory jest OK ale mam potrzebę przesłać ten adres dalej do zupełnie innej klasy, która wygląda tak

BorrowingUsersWindow::BorrowingUsersWindow(QWidget *parent) : QDialog(parent)
{
....

Po co ci adres do bazy danych w Dialogu.
Dialog ma być głupi jak but. Ma wyświetlić coś użytkownikowi, pobrać coś od niego i odesłać te coś gdzieś gdzie to jest potrzebne.

0

@MarekR22:

Po co ci adres do bazy danych w Dialogu.
Dialog ma być głupi jak but. Ma wyświetlić coś użytkownikowi, pobrać coś od niego i odesłać te coś gdzieś gdzie to jest potrzebne

a jak wyświetlę tabelę w dialogu ? bo w sumie o to mi chodzi. Poniżej zrzut, gdzie w tle okno MainWindow, a na pierwszym planie okno QDialog do którego chcę załadować tabelę z danymi - to ma być formularz do wprowadzenia niektórych danych

screenshot-20220609174926.png

1
zkubinski napisał(a):

a jak wyświetlę tabelę w dialogu ? bo w sumie o to mi chodzi. Poniżej zrzut, gdzie w tle okno MainWindow, a na pierwszym planie okno QDialog do którego chcę załadować tabelę z danymi - to ma być formularz do wprowadzenia niektórych danych

Normalnie, tworzysz sygnał który wysyła potrzebne dane, a ktoś inny kto zajmuje się bazą danych odbiera ten sygnał.

Jak włączasz silnik samochodu to nie potrzebujesz wkładać palca pod maskę samochodu.
Naciskasz przycisk (lub przekręcasz kluczyk), a sygnał przewodami idzie do silnika.

0
MarekR22 napisał(a):

Normalnie, tworzysz sygnał który wysyła potrzebne dane, a ktoś inny kto zajmuje się bazą danych odbiera ten sygnał.

Jak włączasz silnik samochodu to nie potrzebujesz wkładać palca pod maskę samochodu.
Naciskasz przycisk (lub przekręcasz kluczyk), a sygnał przewodami idzie do silnika.

ok, tyle, że w MainWindnow też otwieram połączenie do bazy danych, bo tam też jest tabela która musi być załadowana z tej samej bazy. A ja nie chcę niepotrzebnie tworzyć wielu połączeń aby coś się nie poyebauo.

1

@zkubinski generalnie, jak zaczniesz zewsząd zmieniać bazę danych to będzie bajzel.
Więc normalni ludzie tworzą klasę odpowiadającą za bazę i do tej klasy poprzez sygnały/zdarzenia/inne cuda (ale należy wybrać tylko jedna drogę) przychodzą zadania zapisz do bazy to, odczytaj z bazy tamto.
Alternatywa - umrzesz marnie, bo zobacz jedna formatka dodaje klienta, zaś druga wyświetla listę, powiedz mi ile razy na sekundę formatka wyświetlająca ma odświeżać listę? A może tylko wtedy kiedy formatka zapisująca wyśle sygnał dodawania, doda i do wszystkich zainteresowanych wyśle sygnał że lista zmieniona? Generalnie to się załatwia inaczej ale podstawą jest jedna odpowiedzialność.

0
_13th_Dragon napisał(a):

@zkubinski generalnie, jak zaczniesz zewsząd zmieniać bazę danych to będzie bajzel.

ja nie zmieniam bazy danych, bo wiem czym to się wiąże - jeżeli chodzi ci o strukturę tj. tabele, pola. Bo to już psuje integralność - mowa o bazie używanej w produkcji. Co innego jak coś testuję, sprawdzam.

Alternatywa - umrzesz marnie, bo zobacz jedna formatka dodaje klienta, zaś druga wyświetla listę

a teraz naprawdę dobre pytanie - a jak zamierzasz dodać dane, gdy masz bazę podzieloną na wiele tabel ? Twój tok myślenia jest ok, w przypadku, gdy masz jedną tabelę ze wszystkimi polami, a w tych polach dublują się pewne dane. Sztuka zrobienia dobrej bazy, to zrobić w ten sposób aby nie powielać zbędnych informacji czyli można zrobić tabelę, a w tej tabeli klucz obcy do danych w innej tabeli i stąd potrzeba zrobienia okienka do wprowadzenia chociażby imienia i nazwiska (rozpatrując przypadek tabeli która przechowuje tylko imiona i nazwiska) - bym ci pokazał strukturę bazy ale wykroczy to poza tematykę wątku. Jak skończę program, to całość będzie publicznie dostępna i będziesz mógł się z tym zapoznać

1

@zkubinski, wygląda na to że ty czytasz tylko pierwsze zdanie z akapitu a resztę omijasz, w wyniku czego nigdy nie rozumiesz o co chodzi.

0

@_13th_Dragon:

Widzę że nadal nie rozumiesz. Masz formatkę z listą produktów: naciskasz <dodaj> => otwiera się nowa formatka; wpisujesz dane produktu; naciskasz <zapisz i dodaj następny> => dodaje się produkt do bazy i masz tą samą formatkę do ponownego wypełnienia na kolejny produkt. Ale co z listą produktów pod spodem? Na niej się nie pojawił nowy produkt? Kiedy się pojawi? Skąd ta formatka będzie wiedzieć że trzeba odświeżyć listę?

Sam zauważyłeś że odświeżenie samo w sobie nie stanowi problemu, więc czytaj raz jeszcze dopóki nie zrozumiesz o co biega.

jeszcze raz - sam pytasz Skąd ta formatka będzie wiedzieć że trzeba odświeżyć listę? ano stąd, że napiszę sobie funkcję refresh - więc odpowiadam na zadane pytanie, więc nie rozumiem w czym jest problem ?

2

Wystarczy odpowiednio zaimplementować model i problem synchronizacji przestaje istnieć, bo wszystkie podpięte widoki aktualizują się automagicznie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1