Catch – problemy przy testowaniu plików *.cpp – unresolved external symbol

Pojawiły się pewne problemy przy testowaniu plików *.cpp. Dotychczas wszystkie pliki aplikacji objęte testami były zdefiniowane w plikach nagłówkowych *.h, więc problem się nie ujawniał, bo nie był potrzebny żaden kod z plików *.cpp. Dzisiaj postanowiłem napisać test do dość rozbudowanej klasy, której konstruktor został zdefiniowany w pliku *.cpp i niestety pojawił się błąd „unresolved external symbol”. Trochę czasu zajęło mi znalezienie przyczyny, którą był brak dołączonych plików *.cpp zawierających definicje konstruktorów i metod. Rozwiązaniem problemu było dołączenie plików *.cpp aplikacji do projektu test.pro.

Czytaj dalej

Columns Description – edycja liczb opisujących obrazek logiczny

Dodałem klasy odpowiedzialne za opis obrazka, czyli za liczby na górze i z lewej strony. ColumnsDescriptionView odpowiada za wyświetlanie zdefiniowanych liczb opisujących poszczególne kolumny obrazka. Umożliwia edycję poszczególnych liczb poprzez ustawienie w odpowiednim polu TextBoxa. BlockDescription to klasa opisująca jeden blok, wyświetlana jako jedna liczba nad obrazkiem. Opisuje ona ilość Pixeli w jednym, ciągłym bloku. LineDescription to zestaw (std::vector) liczb opisujących daną linię (kolumnę) obrazka. W klasie WholeField przechowywane będą dwa obiekty typu AllLinesDescription – columnsDescription oraz rowsDescription odpowiedzialne odpowiednio za opis wszystkich kolumn oraz wszystkich wierszy obrazka. Na chwilę liczby nad obrazkiem nie są jeszcze powiązane z polem graficznym (DrawingArea).

GitHub

Radio TEA5767 – modyfikacja radia kuchennego

Projekt ten został opublikowany przeze mnie gościnnie na portalu majsterkowo.pl. Publikując projekt w dziale elektronika można otrzymać 50% rabatu na zakupy w sklepie nettigo.pl
Poniżej treść artykułu:

Celem projektu była naprawa rozstrajającego się, tandetnego radyjka bez zmiany wyglądu zewnętrznego. Jako odbiornik zastosowałem scalony moduł odbiornika radiowego z syntezą PLL o symbolu TEA5767. Umożliwia odbiór w paśmie UKF 87,5 – 108 MHz. Jest to układ sterowany cyfrowo poprzez interfejs I2C. Jako sterownik w moim projekcie występuje Atmega8. Początkowo układ miał zawierać wyświetlacz LED i umożliwiać ustawianie dowolnej częstotliwości, jednak zrezygnowałem z wyświetlacza, ponieważ jego zastosowanie wymusiłoby modyfikację wyglądu odbiornika. Ostatecznie projekt umożliwia ustawienie jednej z czterech predefiniowanych stacji radiowych. Wybór stacji po modyfikacji odbywa się za pomocą przełącznika zakresów, natomiast gałka strojenia jest niewykorzystana.
Czytaj dalej

Pixele i Fieldy – DrawingArea obrazka logicznego

Dodałem klasę przechowującą pole z całym rysunkiem i liczbami (WholeField). Klasa ta dziedziczy po klasie abstrakcyjnej DrawingAreaField służącej do przechowywania informacji i metod związanych z polem rysunku (DrawingAreaView). WholeField docelowo będzie dziedziczyć także po BlocksDescriptionField – do przechowywania liczb opisujących obrazek. Klasy abstrakcyjne DrawingAreaField oraz BlocksDescriptionField służą jako interfejsy do komunikacji z odpowiednimi widgetami. Dziedziczą one po RootFeld, która przechowuje wymiary pola oraz deklarację sygnału (signal) dataChanged().
Kliknięcie na polu rysunku ustawia teraz wartość odpowiedniego obiektu Pixel przechowywanego w klasie WholeField. Wewnątrz metody field->setPixel emitowany jest sygnał dataChanged(). Sygnał ten podłączony jest do slotu onDataChanged() w klasie DrawingAreaView. Slot onDataChanged() odpowiada za odświeżenie (przerysowanie) widgetu po zmianie danych wewnątrz obiektu field.
Klasa Pixel reprezentuje jeden prostokąt na obrazku. Posiada adres AddressOnDrawingArea, który identyfikuje położenie Piksela na polu rysunku.

GitHub

Lepsze podpowiadanie składni kodu w Qt Creatorze, czyli ClangCodeModel

Qt Creator domyślnie ma włączone „jakieś” podpowiadanie kodu, które cośtam podpowiada, jednak nie jest ono zbyt dobre. Problem pojawia się, gdy przeciążymy operator[], aby zwracał obiekt z naszego kontenera. Przykład:

// mamy przykładową klasę:
class Pixel
{
public:
	Pixel(int x, int y, bool isVisible = true) : x(x), y(y), visible(isVisible) {}
	bool isVisible(){return visible;}
	void makeVisible() {visible = true;}
	void makeInvisible() {visible = false;}
private:
	int x;
	int y;
	bool visible;
};

// i kontener na obiekty tej klasy:
class LineOfPixels : private std::vector < Pixel >
{
public:
	LineOfPixels(){}
	LineOfPixels(std::vector < Pixel > vectorToCopy) : std::vector< Pixel >(vectorToCopy) {}
	size_t size() {return std::vector < Pixel > ::size();}
	
	Pixel& getPixelAt(int pixelNumber) {return this->at(pixelNumber);}
	Pixel& operator[](const int pixelNumber) {return std::vector < Pixel > ::operator[](pixelNumber);}
};

int main()
{
	int size = 2;
	LineOfPixels lineOfPixels(std::vector <Pixel>(size, Pixel(0, 0)));
	lineOfPixels[1].makeInvisible();
	lineOfPixels.getPixelAt(1).makeVisible();
	
	return 0;
}

Po instalacji Qt Creatora opcja Clang Code Model domyślnie jest wyłączona. Jeśli teraz w programie chcemy odwołać się do obiektu klasy Pixel przez zwykły getter:

lineOfPixels.getPixelAt(1).makeInvisible();

to podpowiadanie składni zadziała normalnie pokazując listę dostępnych składowych na każdym poziomie. Natomiast gdy zrobimy to samo przez przeciążony operator[] jak tutaj:

lineOfPixels[1].makeInvisible();

to podpowiadanie składni zgłupieje i nic nam nie pomoże. Po wpisaniu „lineOfPixels[1].” nie otrzymamy żadnej podpowiedzi.
Aby podpowiadanie składni działało poprawnie w każdych warunkach, trzeba włączyć plugin ClangCodeModel, zaznaczając opcję w Help → About Plugins… → C++ → ClangCodeModel.

Link: Parsing C++ Files with the Clang Code Model

Certyfikat SSL

W końcu po kilku średnio udanych próbach udało mi się skonfigurować certyfikat SSL. Certyfikat wystawiony jest przez letsencrypt.org. Początkowo chciałem skonfigorować go przez automatyczną konfigurację dostępną w panelu serwera hostingowego, jednak nie udało się to z powodu błędu: „Must select more than zero LetsEncrypt entries.”. Kolejna nieudana próba to wykorzystanie FREE SSL Certificate Wizard. Wymagał on wgrania plików na serwer do katalogu „.well-known”, niestety pliki w katalogu zaczynającym się od kropki nie były widoczne z zewnątrz – zwracały 404. Ostatecznie udało się na stronie www.sslforfree.com korzystając z opcji „Manually Verify Domain (DNS)” wykorzystującej do autoryzacji wpisy TXT u rejestratora domeny. Wadą takiego rozwiązania jest konieczność ręcznego odnawiania certyfikatu co 3 miesiące. Na szczęście strona sslforfree.com oferuje wysłanie e-maila z przypomnieniem na tydzień przed wygaśnięciem certyfikatu, miejmy nadzieję, że nie trafi on do spamu.

QT Creator – dodanie niestandardowego widgetu w edytorze interfejsu graficznego

Dodanie customowego widgetu. Z menu po lewej stronie edytora interfejsu przeciągamy obiekt „Widget” w docelowe miejsce. Klikamy prawym przyciskiem myszy na widget → „Promote to …”. W oknie Promoted Widgets wpisujemy do „Promoted class name” nazwę naszej klasy, np. DrawingAreaView, natomiast w polu „Header file” ścieżkę pliku nagłówkowego, np. widgets/DrawingAreaView.h. Klikamy Add, zaznaczamy nowo dodany wpis i klikamy Promote. Nasz widget został dodany w miejsce zwykłego QWidgetu.

Pierwsze widgety, obsługa kliknięć i rysowanie

widgety w central widgecie.png

Dodałem widoki planszy. Struktura wewnętrzna widgetów zaczerpnięta jest z przykładowego projektu o nazwie „scribble” znajdującego się katalogu QT. Na ekranie mojej aplikacji znajdują się trzy widoki (widgety). Jeden (DrawingAreaView) jest główną planszą, na której zaczernia się poszczególne pola, natomiast dwa pozostałe są polami pomocniczymi zawierającymi liczby opisujące długości poszczególnych bloków. Wszystkie trzy rozmieszczone są w centralWidget’cie. Dla widgetu rowsDescription ustawiłem Layout Alignment → Right, a dla columnsDescription Layout Alignment → Bottom. Widget DrawingAreaView obsługuje kliknięcia myszy i rysuje prostokąty w różnych kolorach w zależności od klikniętego przycisku myszy.

Link do opisywanego commitu: https://github.com/ololuki/nonograms/commit/de5430e0d860d54893f93cc4b7b97d82960e2a8e

Git Gui i SSH – konfiguracja

Aby ciągle nie wpisywać hasła do githuba w Git Gui, postanowiłem w końcu skonfigurować połączenie przez SSH. W oknie Git Gui klikamy Help → Show SSH Key → Generate Key. Klucz z textBoxa kopiujemy i przechodzimy na github.com → Settings → SSH and GPG keys → New SSH key, podajemy dowolną nazwę, np. „Git Gui” i wklejamy klucz w polu Key. Wracamy do Git Gui i klikamy Remote → Add. W polu Name podajemy nazwę, np. „nonogramsSSH”, natomiast w polu Location:

git@github.com:USERNAME/REPOSITORY.git

w moim przypadku:

git@github.com:ololuki/nonograms.git

Zaznaczamy Do Nothing Else Now i klikamy Add. Przy kolejnym Pushu wystarczy wybrać Remote:nonogramsSSH i można wysyłać kolejne wersje na GitHuba bez konieczności podawania loginu i hasła.

Layout dla każdego (QWidgetu) czyli co zrobić, gdy kontrolki wewnątrz layoutu nie skalują się wraz z oknem

Dziś trochę o layoutach i definiowaniu interfejsu graficznego. Po dodaniu kilku kontrolek do layoutu zauważyłem, że podczas skalowania okna znajdujące się w jego wnętrzu kontrolki nie zmieniają swojego rozmiaru i kształtu. Efekt, który chciałem osiągnąć to skalowanie kontrolek wewnątrz okna wraz ze zmianą rozmiaru okna. Chwila w kwakania w Duck Duck Go zaprowadziła mnie do rozwiązania problemu. Mianowicie każde okno (QMainWindow) i każdy obszar typu QScrollArea powinien mieć ustawiony layout. W edytorze interfejsu po prawej stronie mamy okno z drzewem widżetów. Dla nowo stworzonego projektu zaraz pod elementem MainWindow znajduje się centralWidget. Jest to serce całego interfejsu, najważniejszy widget w oknie. Po jego lewej stronie znajduje się ikonka layoutu z czerwonym znacznikiem informującym, że layout nie został ustawiony. Aby ustawić layout wystarczy kliknąć prawym przyciskiem myszy na MainWindow → Lay Out → Lay Out Horizontaly (bądź inny wybrany przez nas layout). Można też tego dokonać klikając na MainWindow a następnie ikonkę Lay Out Horizontaly na pasku ponad oknem edycji GUI albo używając skrótu klawiszowego CTRL + H.

Po ustawieniu layoutu zawartość okna powinna zmieniać rozmiar razem z nim: