Archiwum miesiąca: maj 2017

Podsumowanie konkursu daj się poznać 2017.

Konkurs daj się poznać dobiegł końca. Celem konkursu było rozwijanie projektu programistycznego i pisanie bloga na ten temat. Aplikacja, którą pisałem to obrazki logiczne.

GitHub

W przygotowywanej przeze mnie aplikacji udało się zaimplementować edytor pola rysunkowego i liczb opisujących. Jest też możliwość zapisu i odczytu pola rysunkowego do i z plików w formacie Json. Przy implementacji obsługi plików inspirowałem się tym programem: Simple Drawing App.

Liczba postów oraz commitów rosła wykładniczo w stosunku do mijającego czasu. Może to wynikać z tego, że na początku walczy się z prostymi, ale jednak nieznanymi problemami. Robienie czegoś pierwszy raz zajmuje najwięcej czasu. Dodatkowo można wejść w ślepy zaułek jakiegoś zagadnienia i zmarnować tam trochę czasu – tak było z moją przygodą z wxWidgets. Początkowo chciałem wykorzystać właśnie tę bibliotekę, bo już coś tam miałem w niej napisane, bo licencja trochę bardziej liberalna, bo kontrolki bardziej natywne niż w Qt, ale stary kod nie bardzo chciał mi działać na nowej wersji biblioteki i w końcu zdecydowałem się na użycie Qt i Qt Creatora. Pakiet Qt zadziałał praktycznie od razu, pomijając drobne komplikacji z instalacją debugera. Innym problemem, która zmarnował trochę mojego cennego czasu było zamknięcie dotychczasowego serwera hostingowego, przez co chcąc nie chcąc musiałem walczyć z przenosinami bloga.

Dokumentowanie projektu oraz opisywanie problemów i ich rozwiązań może być przydatne w przyszłości, kiedy będziemy chcieli coś zrobić drugi raz, albo ponownie skonfigurować środowisko po reinstalacji systemu. Wpis na blogu może być wtedy bardzo pomocną ściągą. Tym bardziej, że trudniej go zgubić niż jakieś notatki na kartkach czy w plikach na dysku. Po drugie taki publiczny wpis jednak musi reprezentować sobą jakieś minimum estetyki i być zrozumiały dla czytelników, więc dużo łatwiej będzie w przyszłości odświeżyć sobie wiedzę o jakimś zagadnieniu z blogowego wpisu niż z nabazgranej na szybko karteczki.

Generalnie warto było wziąć udział w konkursie. Uczestnictwo motywowało do mniej lub bardziej systematycznej pracy. Konkurs się kończy ale doświadczenie i wartościowe wpisy na blogu pozostają.

Qt Deployment, czyli tworzenie wersji release ze wszystkimi plikami *.dll

QT Creator domyślnie tworzy aplikacje z linkowaniem dynamicznym (shared). Jeśli korzystamy z darmowej, otwartoźródłowej wersji QT to jest to zgodnie z licencją jedyna opcja. W związku z tym razem z publikowanym plikiem *.exe projektu trzeba dostarczyć skompilowane pliki biblioteki od których jest on zależny. Pomoże nam w tym windeployqt.exe (Qt Deployment Tool) znajdujący się w katalogu Qt\Qt5.8.0\5.8\msvc2015_64\bin. Odpalamy ten plik w cmd.exe, jako argument podając ścieżkę do pliku wykonywalnego projektu, np. C:\QT Projects\nonograms\app\release\project1.exe. Po tej operacji w folderze release znajdują się wszystkie pliki potrzebne do działania aplikacji, a nawet więcej. Aby usunąć niepotrzebne pliki wystarczy odpalić aplikację project1.exe i w czasie jej działania spróbować usunąć pozostałe pliki z folderu. Niepotrzebne pliki powinny zostać usunięte a wymagane przez aplikację zostaną pominięte. Sposób ten opisany jest tutaj: Deploy an Application on Windows

cd C:\Qt\Qt5.8.0\5.8\msvc2015_64\bin
windeployqt.exe "C:\QT Projects\nonograms\app\release\nonograms.exe"

Więcej na stronie QT: Qt for Windows – Deployment

Diamentowy problem i dziedziczenie wirtualne

Język C++ oferuje możliwość dziedziczenia wielokrotnego ze wszystkimi wadami i zaletami tego rozwiązania. W przypadku dziedziczenia po kilku klasach mających wspólnego przodka może pojawić się problem z diamentem.

Błąd „base class is ambiguous” świadczy o tym, że dwie klasy, po których dziedziczy nasza „najmłodsza klasa” są potomkami wspólnego przodka. Jeśli obie te klasy dziedziczą po tym przodku niewirtualnie to otrzymamy wspomniany wcześniej błąd, ponieważ klasa dziadka zostanie dodana dwa razy, więc będzie zduplikowana. Rozwiązaniem problemu będzie zastosowane dziedziczenia wirtualnego w naszych dwóch klasach.

W moim projekcie dziedziczenie wirtualne zostało zastosowane przy dziedziczeniu podstawowej klasy pola – RootField. Klasa RootField przechowuje tylko główne, wspólne parametry pola takie jak szerokość i wysokość. Następnie mamy dwie klasy po niej dziedziczące – DrawingAreaField oraz BlocksDescriptionField. Jeden dostarcza metody dla obszaru rysowania (DrawingArea) a drugi dla opisów bloków (RowsDescription i ColumnsDescription). Klasa WholeField łączy te elementy w całość i pozwala odwoływać się do pola roboczego jako całości, np. z poziomu solvera.

class RootField : public QObject
{
	Q_OBJECT
public:
	RootField();
	RootField(size_t width, size_t height) : width(width), height(height) {}
	size_t getWidth() {return width;}
	size_t getHeight() {return height;}
signals:
	void dataChanged();
private:
	size_t width;
	size_t height;
};


class DrawingAreaField : public virtual RootField
{
public:
	virtual Pixel getPixel(AddressOnDrawingArea address) = 0;
	virtual void setPixel(Pixel pixel) = 0;
};


class BlocksDescriptionField : public virtual RootField
{
public:
	virtual BlockDescription getBlockDescription(AddressOnBlocksDescription address) = 0;
	virtual void updateBlockDescription(BlockDescription blockDescription) = 0;
	virtual void insertDescriptionBefore(BlockDescription blockDescription) = 0;
	virtual void addDescriptionAtEnd(BlockDescription blockDescription) = 0;
	virtual void deleteDescription(BlockDescription blockDescription) = 0;
	virtual size_t numberOfBlocksInColumn(size_t columnNumber) = 0;
	virtual size_t columnsDescriptionHeight() = 0;
	virtual bool isDefinedColumnDescriptionAt(size_t line, size_t count) = 0;
};


class WholeField : public DrawingAreaField, public BlocksDescriptionField, virtual public RootField
{
};


class WholeFieldImpl : public WholeField
{
public:
	WholeFieldImpl(size_t width, size_t height) : RootField(width, height)
	{
		array = ArrayOfPixels(width, height);
		columnsDescription = AllLinesDescription(AddressOnBlocksDescription::orientation::VERTICAL, width);
	}
	virtual Pixel getPixel(AddressOnDrawingArea address) override;
	virtual void setPixel(Pixel pixel) override;
	virtual BlockDescription getBlockDescription(AddressOnBlocksDescription address) override;
	virtual void updateBlockDescription(BlockDescription blockDescription) override;
	virtual void insertDescriptionBefore(BlockDescription blockDescription) override;
	virtual void addDescriptionAtEnd(BlockDescription blockDescription) override;
	virtual void deleteDescription(BlockDescription blockDescription) override;
	virtual size_t numberOfBlocksInColumn(size_t columnNumber) override;
	virtual size_t columnsDescriptionHeight() override;
	virtual bool isDefinedColumnDescriptionAt(size_t line, size_t count) override;
private:
	ArrayOfPixels array;
	AllLinesDescription columnsDescription;
};

Kolejnym problemem okazało się wywoływanie konstruktorów parametrycznych z klasy bazowej (dziedziczonej wirtualnie) za pomocą listy inicjalizacyjnej. Okazało się, że wywołanie konstruktora klasy bazowej (dziedziczonej wirtualnie) za pomocą listy inicjalizacyjnej musi znajdować się w najmłodszej klasie – w tej, której obiekt zostanie utworzony za pomocą konstruktora parametrycznego. W sytuacji gdy mamy hierarchię klas, np:

class A {
	int i;
public:
	A(int k) {}
};

class B : virtual public A {
public:
	B(int i) : A(i) {}
};

class C :   public B {
public:
	//C(int i) : B(i) {} // zostanie wywołany tylko konstruktor klasy B, konstruktor A(int k) nie zostanie wywołany, gdyż klasa A jest wirtualnie dziedziczona, zostanie wywołany konstruktor domyślny klasy A
	C(int i) : B(i), A(i) {}	// zostanę wywołane oba konstruktory parametryczne
};

Linki na temat:
multiple-inheritance-from-two-derived-classes
how-can-i-avoid-the-diamond-of-death-when-using-multiple-inheritance
Multiple_inheritance#The_diamond_problem
Virtual_inheritance

Wywoływanie konstruktorów z klasy wirtualnie dziedziczonej za pomocą listy inicjalizacyjnej.
order-of-constructor-call-in-virtual-inheritance

Qt Creator – „Error while saving file: Cannot write file … Disk full?” – rozwiązanie

Czasami podczas próby skompilowania i uruchomienia programu w Qt Creatorze pojawia się błąd „Error while saving file: Cannot write file … Disk full?”. Jest to objaw błędu w Clang Code Model. Pomaga wtedy zabicie procesu clangbackend.exe. Czasami też pojawiają się dziwne pliki w katalogu z plikami źródłowymi o rozszerzeniach złożonych z losowych znaków, np. *.zA5996 – trzeba je usunąć, bo mogą powodować problemy przy kompilacji.

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.