[C++, QT] dobranie odpowiednich klas & czytanie z pliku

0

Witam!

Mam do zaimplementowania pewien algorytm matematyczny, który wykonuje obliczenia na danych. Większym jednak dla mnie problemem jest stworzenie GUI, odczyt i zapis etc. Zdecydowałem się na środowisko Qt Creator i język C++. Po stworzeniu projektu znajdują się w nim cztery pliki: mainwindow.ui, mainwindow.h, main.cpp, mainwindow.cpp.

Dodałem dwa przyciski, tj. "start" i "wyjście". Do przycisku wyjście akcję dodałem jako "Modyfikuj sygnały / slotu", zaś do "start" wykorzystałem "Przejdź do slotu" clicked().

Stworzyłem w pliku mainwindow.cpp następujący kod:

void MainWindow::on_pushButton_clicked()
{ ifstream in("plik.txt"); }

Dodałem również na górze 'iostream' oraz 'fstream':

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include <fstream>

Dostałem następujący błąd:

In member function 'void MainWindow::on_pushButton_clicked()':

D:\Qt\2010.05\tmp01-build-desktop..\tmp01\mainwindow.cpp
'ifstream' was not declared in this scope
expected ';' before 'in'

Właściwie to mam dwa problemy. Jeden związany jest z wczytaniem pliku po wciśnięciu przycisku, natomiast drugi z programowaniem obiektowym. Dokładniej rzecz ujmując to mógłbym wszystko to rozwiązać jako jedną długą sekwencję zapisaną w postaci kodu uruchamianego po wciśnięciu przycisku. Kod taki rozpoczynałby się wczytaniem pliku do zmiennych lokalnych funkcji on_pushButton_clicked(), operacjami na nich i zapisaniem wyników do pliku. Nie wygląda mi to jednak na rozwiązanie eleganckie.

Kolejna możliwość to stworzenie dodatkowej klasy, np. myData. Tą klasę mogę dodać w pliku main.cpp albo mainwindow.cpp. Domyślam się, że lepiej jest to zrobić w mainwindow.cpp oraz zawrzeć nagłówek klasy w mainwindow.h. W pliku mainwindow.cpp znajdują się: include, konstruktor, destruktor, funkcja on_pushButton_clicked(). Nie wiem, czy miejsce, w którym umieszczę swoją klasę ma znaczenie, czy nie, ale mogłoby to być zaraz po destruktorze MainWindow.

Taka klasa miałaby konstruktor (przyjmujący jako daną wejściową zmienną plikową z plikiem wejściowym - przekazanie przez referencję), który z pliku wejściowego czytałby liczbę elementów n, dwie macierze n x n (zmienne integer) i parę dodatkowych zmiennych liczbowych.

I teraz rzecz wygląda w ten sposób, że są trzy algorytmy, obliczające dane wyjściowe oraz jeden parametr, którego wyliczenie umożliwia stwierdzenie, który z algorytmów obliczył wartość w sposób najbliższy wartości prawdziwej. Ja mam zamiar zaimplementować jeden z algorytmów i ów parametr (metrykę), natomiast koleżanka pozostałe dwa algorytmy. Możliwe jest, że niektóre z tych algorytmów będą potrzebowały przekształcenia macierzy do innej postaci, więc konieczne będą dodatkowe zmienne (tablicowe lub inne), stąd też może być konieczna klasa potomna, z macierzami n x n, ale innymi wartościami, wyliczanymi np. w konstruktorze tej klasy potomnej.

Innymi słowy, gdyby rozwiązać to obiektowo, to zamiast wrzucać cały kod do funkcji on_pushButton_clicked(), byłoby to tylko wywołanie pewnych funkcji, tj. konstruktora myData(), funkcji calculate (będącej funkcją klasy myData), która to funkcja stworzyłaby plik wyjściowy, następnie może np. showData (czyli funkcji wyświetlającej poprzednio stworzony plik wyjściowy, np. w postaci pól podobnych do tych w MS Excelu). Funkcja showData może być też rozwiązana inaczej, ponieważ calculate wyliczałoby dane przy użyciu konstruktora lub funkcji klasy pochodnej dla konkretnego z trzech algorytmów. Innymi słowy dane pokazywane przez showData to nie muszą być dane pobrane z pliku wyjściowego, ale mogą być np. wynikowe macierze którejś z klas pochodnych albo klasy głównej.

Jest jeszcze kwestia podzielenia tego projektu na różne pliki z poszczególnymi klasami. Mogę wszystkie klasy wrzucić do pliku mainwindow.cpp, ale zapewne nie byłoby to rozwiązaniem eleganckim. Co do zaś dzielenia projektu na różne pliki, odpowiedzialne za poszczególne klasy, to nie mam w tej kwestii zbyt dużego doświadczenia, właściwie to prawie żadne.

Tak sobie myślę, że poprawne i logiczne podzielenie całego projektu na odpowiednie klasy (oraz właściwe przydzielenie metod i parametrów) to warunek konieczny, bez którego nie mam za bardzo co się zabierać za implementację. Nawet jeśli będą jakieś późniejsze zmiany czy problemy, stosunkowo niewiele kodu będę musiał zmienić, jeśli odpowiednio rozpocznę tworzenie projektu przy pomocy klas. Stąd też prosiłbym o jakieś sugestie, może linki do przydatnych tutoriali lub jakiś szkielet kodu, zawierający podział na klasy, albo sposób rozwiązania klas przy pomocy wielu plików źródłowych lub przynajmniej jakiekolwiek wskazówki. Nie zajmuję się programowaniem i nie planuję tego w przyszłości, ale chciałbym skończyć ten projekt, który zrobić muszę, stąd też wdzięczny będę za wszelką pomoc :).

A ponieważ trochę ciężko może być ją udzielić, nie wiedząc, co tak naprawdę te algorytmy matematyczne mają robić, może nadmienię tu krótko o celu działania programu. Otóż daną wejściową jest macierz n x n, zawierająca informacje o n osobach, z których konkretna osoba może znać drugą osobę (ale nie musi), a więc macierz wskazuje w formie zera na znajomość lub jedynki na brak takiej znajomości.

Ponieważ istnieje możliwość, że np. spośród 50 osób, 15 zna się bardzo dobrze i pozostałych 35 również zna się bardzo dobrze, natomiast pomiędzy obiema grupami jest niewielki związek (np. dwie lub trzy osoby znają się między dwoma grupami). Stąd też algorytm miałby być w stanie pokazać takie grupy na podstawie samej tylko znajomości kontaktów między osobami. Taki plik wyjściowy będzie też w odpowiednim formacie, żeby program NetDraw (UciNet) mógł zwizualizować taką sieć, już podzieloną.

Z góry dzięki za wszelkie pomocne sugestie
Pozdrawiam!

1

Uwaga! Nie przeczytałem nic od ostatniego wersu treści błędu do końca postu. Błąd spowodowany pewnie brakiem using namespace std; bądź std::ifstream. Istnieje też możliwość że on_pushButton_clicked() nie jest zdeklarowana jako slot ale stawiam raczej na brak std.

1

a gdzie inklud QApplication i QPushButton? Wklej kody wszystkich plików to będzie coś widać.

0

Dzięki za odpowiedzi!

Trochę czasu minęło, odkąd ostatnio programowałem. W tej chwili projekt się kompiluje, więc prosiłbym o przeczytanie dalszej części pierwszego postu również.

Pliki wyglądają na razie następująco:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void on_pushButton_clicked();
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include <fstream>

using namespace std;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    //load D:\moje_dane\zachary.dat
    ifstream in("plik.txt");
    /*
    int licznik=1;
    string wers[100];

    while(getline(in, wers[licznik])){
                      cout<<wers[licznik]<<endl;
                      licznik++;
                      }
    system("PAUSE");
    */
}

main.cpp

#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

Czyli w zasadzie jest niewiele więcej, niż w nowoutworzonym projekcie.

Na razie, zamiast dokończyć czytanie pliku - co też będę musiał zrobić w najbliższym czasie - zastanawiałem się bardziej nad klasami, których bym potrzebował. Doszedłem do wniosku, że w algorytmie potrzebne są trzy klasy (choć może dałoby się to rozwiązać inaczej, w bardziej elegancki sposób). Trochę nie bardzo wiem, jak rozwiązać wyprowadzenie wyników z klasy drugiej i trzeciej, bo przecież nie jako wartość zwracaną przez konstruktor; może najlepiej stworzyć akcesor, który będzie zwracał dane wyliczone przez konstruktor, na podstawie danych pobranych jako argument konstruktora. Wyglądałoby to na przykład tak:

class myData
{
myData(); // konstruktor pobiera dane z pliku tekstowego
mainMatrix; //dynamicznie tworzona macierz o wymiarach n x n
//być może powyżej trochę kłopotu mi sprawi dynamiczne stworzenie macierzy, bo nigdy nie byłem dobry we właściwym alokowaniu pamięci, choć to w sumie prosta sprawa
//...
}

class wagiOrazDystans //albo zamiast klasy funkcja, będąca metodą klasy myData (?)
{
//input - macierz n x n
//output - dwa wektory (macierze jednowymiarowe) o wymiarze n, jedna to wagi, druga to wartości dystansów
}

class wartosciPosrednie
{
//input - wektor wag oraz macierz n x n
//output - macierz n x n (tym razem macierz wartości pośrednich)
}

Z góry dzięki za wszelką pomoc,
pozdrawiam!

1

Drogi autorze, skoro zaprzęgasz do roboty takie działo jak Qt wydaje się być "śmiesznym" (mało odpowiednie słowo, nie chcę nikogo wyśmiewać:) ) stosowanie standardowych funkcji bibliotecznych (np do obsługi plików) skoro Qt udostępnia takie piękne klasy (równie przenośne jak ono całe) QFile, QFileDialog itd. Zachęcam do przeglądania dokumentacji w miarę potrzeb (tzn. chcesz wczytać plik? Zapytaj googla o pliki w Qt i za 2 min wiesz już jak w Qt to obsłużyć).

W Qt Creator jest fajna możliwość instalacji pomocy (znajduje się ona tam domyślnie) posiada miłą przeglądarkę pomocy oraz szybką wyszukiwarkę. Gdy będziesz miał chwilkę wolną zachęcam również do przejrzenia example dołączonego również do QtCreator, co prawda nie skupia się on tak bardzo na konkretnych problemach bibliotecznych tylko raczej na prezentacji możliwości Qt, to jednak zawsze coś z tych kodów można wyciągnąć.

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