Skocz do zawartości

Kurs Qt – #4 – aplikacja mobilna, łączność Bluetooth z Arduino


Pomocna odpowiedź

Kurs Qt – #4 – aplikacja mobilna, łączność Bluetooth z Arduino

Za nami pierwsza, prosta aplikacja mobilna. Pora, aby pójść krok dalej i nawiązać bezprzewodowe połączenie między telefonem, a Arduino. Do wykonania tego ćwiczenia potrzebny będzie telefon z Androidem w wersji 4.1 lub wyższej oraz moduł w standardzie Bluetooth Classic (wersje od 1.0 do 3.0), czyli np. popularny HC-05.

UWAGA, to tylko wstęp! Dalsza część artykułu dostępna jest na blogu.

Przeczytaj całość »

Poniżej znajdują się komentarze powiązane z tym wpisem.

Link do komentarza
Share on other sites

 Nie miałem jeszcze czasu na zapoznanie z QT w praktyce (bardzo podpasował mi Processing 3) ale artykuły prześledziłem. Ogólnie ujmując to wykonany kawał dobrej roboty. 

Edytowano przez slon
błąd z mojej strony
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Cytat

Następnie w pliku mainwindow.h dodajemy:


#include <QbluetoothDeviceDiscoveryAgent>

Powinno być chyba QBluetoothDeviceDiscoveryAgent

 

Cytat

Zaimplementujmy teraz nasz slot captureDeviceProperties(QBluetoothDeviceInfo). W tym celu, w pliku mainwindow.h, pod słowem kluczowym private slots: stwórzmy deklarację slotu:


private slots:

  ...

  void deviceDiscovered(const QBluetoothDeviceInfo &device);

 

A tutaj captureDeviceProperties zamiast deviceDiscovered.

Poza tym świetny artykuł, czekam na więcej 🙂

 

Edit: Jeszcze tutaj Bluetooth z dużej litery:


MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
  ...
  this->socket = new QbluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this);
}

 

Edytowano przez Karrol
  • Lubię! 2
Link do komentarza
Share on other sites

Kilka uwag i pytań

1. Odnośnie metody do łatwiejszego wysyłania wiadomości:

metodę void sendMessageToDevice(QString message); umieściłem u siebie pod private slots, bo myślałem, że w tekście jest błąd i program działa u mnie poprawnie. Wszystkie metody zawsze dodawaliśmy pod private slots, a pod private obiekty, przynajmniej tak ja to rozumiałem. Tymczasem w backupie jest tak jak w instrukcji. Jaka jest różnica pomiędzy nimi, skoro program działa niezależnie od tego pod którym słowem deklarujemy metody?

Druga uwaga:

Cytat


void MainWindow::on_pushButtonSearch_clicked() {
  qDebug() << "Szukanie...";
  this->discoveryAgent->start(); // rozpoczęcie wyszukiwania
}

Teraz musimy przechwycić właściwości znalezionych urządzeń (jeśli na takie trafimy). Idealnie nadaje się do tego mechanizm sygnałów i slotów. Dodajmy kolejną linię kodu:



connect(this->discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), this, SLOT(captureDeviceProperties(QBluetoothDeviceInfo)));

 

Tekst sugeruje, że funkcja connect powinna zostać wywołana w slocie on_pushButtonSearch_clicked()

Tymczasem gdy tak zrobimy, podczas szukania urządzenia się dublują mimo czyszczenia ComboBoksa. 

Ta linia powinna się znaleźć w konstruktorze MainWindow, wtedy wyszukiwanie będzie działało poprawnie. Tak jest w zipie z projektem, ale dobrze by było w tekście też poprawić.

Pozdrawiam i dzięki za kolejny świetny materiał

 

Edytowano przez kabaczek
Link do komentarza
Share on other sites

@Matthew11 istnieje szansa na jeszcze jeden artykuł, gdzie będzie wyjaśnione tworzenie aplikacji z wieloma ekranami i operacjami pomiędzy nimi?

Chciałem stworzyć w MainWindow przycisk, który otworzy mi nowe okno. Z nowego okna będę chciał wysyłać sygnały przez BT na arduino. Ale na początek w nowym oknie chciałem dać przycisk, za pomocą którego będę mógł wpisać wiadomość do loggera z pierwszego okna - czyli muszę wywołać metodę addToLogs dla mojego "głównego obiektu", znajdując się w innym obiekcie?

Dodałem z poziomu projektu nową klasę formularza Qt Designer o templatce Dialog without buttons.

W mainwindow.h przesunąłem metodę addToLogs pod public.

Utworzyłem nowy slot, który tworzy i "wykonuje" obiekt po naciśnięciu przycisku 

void MainWindow::on_pushButtonOpenControl_clicked()
{
    control control(this);
    this->hide();
    control.setModal(true);
    control.exec();
    qDebug() << "Wrócilem";
    this->show();
}

W control.cpp mam działający przycisk, który chowa to okno i wraca do mainWindow za pomocą funkcji hide() .

Ale zupełnie nie mam pomysłu  co dać do slotu od przycisku z komunikatem.

Wrzucam jeszcze swój projekt jakby komuś się chciało go przejrzeć.

bzdura4.zip

Link do komentarza
Share on other sites

Nie wiem, czy to najwłaściwsza metoda i czy dobrze zrozumiałem problem, ale jeżeli w jakimś obiekcie tworzysz obiekt podrzędny, który powinien wywoływać metodę obiektu nadrzędnego, to metodę tę możesz potraktować jako slot. Po utworzeniu nowego obiektu, funkcją connect możesz połączyć sygnał pochodzący z obiektu podrzędnego ze slotem w obiekcie nadrzędnym. Wówczas „wywołanie" metody obiektu nadrzędnego sprowadza się do emisji odpowiedniego sygnału w obiekcie podrzędnym.

Link do komentarza
Share on other sites

@kabaczek Problemów jest kilka:

  1. miejsce stworzenia obiektu klasy control
  2. wywołanie metody exec() dla QDialog
  3. problem przekazania zależności klasy MainWindow do pochodnego QDialog

Pierwszy i drugi problem są ze sobą powiązane. Pierwszy dotyczy miejsca, w którym tworzysz obiekt Twojej implementacji QDialog w postaci klasy control co implikuje drugi problem. Tworzysz obiekt w metodzie void MainWindow::on_pushButtonOpenControl_clicked() - na stosie - więć po wyjściu z metody, ten obiekt zostanie usunięty a okno zamknięte automatycznie. Nie jest to nic złego - tylko użycie jest niezgodne z założeniem gdyż dalej wywołanie control.exec() jest blokujące tj. tam odpala się lokalna pętla zdarzeń dla QDialog więc cokolwiek byś nie zrobił, nie skomunikujesz nowo otwartego okna z głównym. Bo program kręci się obecnie tylko w nowym oknie. 

Trzeci problem polega na tym, że trzeba jakoś wystawić funkcjonalność addToLogs() do nowego okna. Tutaj są dwa rozwiązania: stworzenie sygnału w nowym oknie z jednoczesnym przerobieniem metody addToLogs() na publiczny slot i połączenie obu w MainWindow za pomocą connect (opisane poniżej) albo stworzenie klasy Logger i jej obiektu w MainWindow i przekazanie go za pomocą konstruktora klasy control (tzw. dependency injection) - co jest bardziej eleganckim rozwiązaniem.

Co trzeba zrobić żeby przerobić program, tak aby spełnił Twoje oczekiwania:

1. zdefiniować addToLogs() w MainWindow jako slot:

public slots:
    void addToLogs(QString message);

2. stworzyć obiekt klasy control jako member MainWindow:

private:
    // ...
    control control;

 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow),
      control(this) // <--

3. W klasie control dodać sygnał (można go nazwać inaczej żeby odróżnić go od slotu w MainWindow):

signals:
    void addToLogs(QString message);

od razu wyemitować przykładowy log w void control::on_pushButtonMessage_clicked():

void control::on_pushButtonMessage_clicked()
{
    emit addToLogs("Loguje...");
}

4. w konstruktorze MainWindow dodać connect:

connect(&control, &control::addToLogs, this, &MainWindow::addToLogs);

(Używam formy connect z Qt5 (zapis funktorowy) - wyjaśnienie w kolejnych częściach kursu.)

5. w void MainWindow::on_pushButtonOpenControl_clicked() zmienić implementację na nie blokującą (po prostu otwarcie okna):

void MainWindow::on_pushButtonOpenControl_clicked()
{
    control.setModal(true);
    control.open();

    qDebug() << "Wrócilem";
}

Na tym etapie masz możliwość logowania informacji.

6. Żeby chować główne okno musisz dodać w powyższym chowanie okna  (this->hide()) a w konstruktorze MainWindow:

connect(&control, &control::accepted, this, &MainWindow::show);

a void control::on_pushButtonOpenMainWindow_clicked() zaimplementować w ten sposób:

void control::on_pushButtonOpenMainWindow_clicked()
{
    hide();
    emit accepted();
}

Czyli de facto powiązać sygnał QDialog::accepted z pokazaniem głównego okna (z ówczesnym schowaniem pochodnego okna).

(Jakiegoś kroku może brakować - najpierw zaimplementowałem, a dopiero potem opisałem.)

 

Natomiast tak już poza konkursem. W obecnej chwili nie polecam wykorzystywania QtWidgets do nowych projektów tylko wykorzystanie QtQuick. Budowanie UI jest tam znacznie szybsze i daje dużo większe możliwości.  

  • Lubię! 2
Link do komentarza
Share on other sites

@Matthew11 dzięki za rozwiązanie, jutro sprawdzę czy jestem w stanie to zrozumieć i zaprogramować.

tylko jedna kwestia - w klasie control tworzysz kolejny raz metodę addToLogs - a ja właśnie chciałem tego uniknąć i "wywołać" od razu tę metodę dla klasy MainWindow - jest to pewnie poprawne, ale wydaje mi się mało eleganckie - gdyby to była bardziej skomplikowana funkcja to pisanie jej jeszcze raz wydaje mi się zbędne, nawet jeśli miałoby to polegać wyłącznie na skopiowaniu kodu z klasy nadrzędnej.

 

Edit:

chwila, chyba źle zrozumiałem. Nie tworzysz metody addToLogs, tylko sygnał - i nie muszę tworzyć ciała metody w control.cpp, jedyne co z tym robię to jej emitowanie, prawda?

void control::on_pushButtonMessage_clicked()
{
    emit addToLogs("Loguje...");
}

 

 

Edit2:

Cytat

5. w void MainWindow::on_pushButtonOpenControl_clicked() zmienić implementację na nie blokującą (po prostu otwarcie okna):

 

O ile dobrze pamiętam, to ten sposób (otwarcie dwóch okien) powodował, że mi się aplikacja crashowała, gdy próbowałem ją odpalić na androidzie. Nie wspomniałem o tym, ale przede wszystkim właśnie na aplikacji smartfonowej mi zależy.

Edytowano przez kabaczek
Link do komentarza
Share on other sites

2 godziny temu, kabaczek napisał:

chwila, chyba źle zrozumiałem. Nie tworzysz metody addToLogs, tylko sygnał - i nie muszę tworzyć ciała metody w control.cpp, jedyne co z tym robię to jej emitowanie, prawda?

Dokładnie, addToLogs() jest sygnałem który emitujemy a łapiemy go w MainWindow.

2 godziny temu, kabaczek napisał:

O ile dobrze pamiętam, to ten sposób (otwarcie dwóch okien) powodował, że mi się aplikacja crashowała, gdy próbowałem ją odpalić na androidzie.

Pod Linuxem nie miałem żadnych problemów, więc spróbuj uruchomić aplikację pod debuggerem i zobacz gdzie konkretnie jest problem, może jest to po prostu jakiś problem w kodzie lub Android wymaga szczególnej obsługi okien.

2 godziny temu, kabaczek napisał:

Nie wspomniałem o tym, ale przede wszystkim właśnie na aplikacji smartfonowej mi zależy.

Skoro Twoim targetem jest Android to tym bardziej zachęcam do wykorzystania QtQuick zamiast QtWidgets. 

Edytowano przez Matthew11
  • Lubię! 2
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.