NAssist

This time I would like to publish my application that I wrote some time ago and I’ve been actively using. I call it NAssist. Purpose of this application is to provide an on demand window (accessed with global keyboard shortcut) that allows to enter some commands whose effects would be displayed inside it.

Basic application window

Basic application window

Application is written in Python 3 with PyQt5 and PyWin (for Windows) or python-xlib (for Linux). PyQt is used as an application framework while PyWin or python-xlib are needed for handling global hotkeys.

Usage

Global hotkeys are used to call application which is normally hidden in the background with icon in Notification Area or Tray (both on Windows and Linux). Application window can be called with predefined global hotkeys – CTRL + SHIFT + SPACE. On the other hand the window can be hidden with ESCAPE hotkey while focused.

Application in default mode appears only with one Command field which should be already focused by previously pressing mentioned hotkey combination (CTRL + SHIFT + SPACE). If it’s not, it can be made so by pressing them again. In this state application is ready to accept commands which are defined in __init__ function in mainwindow.py file. Those commands come from modules package.

Example of translation from English to Polish

Example of translation from English to Polish

Basic commands that come with the application are:

  • ap {text} – English -> Polish translator (google translate),
  • pa {text} – Polish -> English translator (google translate),
  • np {text} – German -> Polish translator (google translate),
  • pn {text} – Polish -> German translator (google translate),
  • sjp {word} – Polish language dictionary (sjp.pl).

To use one of those commands simply write them in Command field with arguments after space. The result of “ap I translate to Polish” is presented in the image on the right. As shown, the result appears right below the Command field, also the window is enlarged to make the result visible.

To quickly delete everything from Command field there is an additional hotkey – SHIFT + DELETE.

Writing own modules

Writing own modules is pretty straightforward. It requires a module to handle following events which are defined in BaseModule.py file:

  • onKeyPress(event) – key has been pressed inside application window, event represents PyQt event,
  • onTextUpdate(text) – argument text has been updated for given module (from command “ap text” only “text” will be passed,
  • onActivate() – current module has been activated,
  • onDeactivate() – current module has been deactivated (new module will be activated if command was changed, not removed),
  • onClose() – application is closing.

The basic commands are implemented using some more specialized base classes that are available for extension:

  • TranslatorModule – uses google translator to provide web page with results via “https://translate.google.com/#{}/{}/{}” pattern,
  • WebModule – is used to implement above module, can be used to add other webpages as commands.

Download and requirements

Application is already public on GitHub with GPL2 license.

Requirements for Windows:

Requirements for Linux:

  • Python 3
  • PyQt5 matching Python version
  • python3-xlib (pip install python3-xlib)

Raytracer

Jak zwykle po długiej nieobecności w końcu pojawia się kolejna notka. Tym razem chciałbym zaprezentować Raytracer, który napisałem w ramach laboratorium z Zaawansowanej grafiki komputerowej. Program oprócz standardowego śledzenia promieni potrafi również generować teksturę proceduralną cegły dla podanego atrybutu (tę część napisałem z kolegą Adamem Jordankiem) oraz prowadzić obliczenia na wielu komputerach wykorzystując model klient-serwer.

Aplikacja do renderowania grafiki wykorzystuje między innymi moją własną biblioteczkę matematyczną, proste kontenery, a oprócz tego – do obsługi klienta i serwera –  sockety i wątki (w zależności od systemu ich odpowiednie implementacje).  Implementacja Raytracera wykorzystuje algorytm przecięcia promienia z trójkątem oraz implementację kd-tree znalezioną na tej stronie, które według mojego przekonania są na tyle wydajne i stabilne, że nie było sensu tego modyfikować. Wykorzystywany model oświetlenia jest efektem prób i błędów, ale myślę że wygląda całkiem znośnie. Zaimplementowane są wszystkie podstawowe elementy takie jak cienie, odbicia czy refrakcja.

Z ciekawszych rzeczy, które można znaleźć w kodzie to:

  • obsługa linii poleceń, która opiera się przede wszystkim na liście poleceń, gdzie każde polecenie zawiera nazwę i opis parametru oraz wskaźnik do funkcji, która implementuje jego obsługę.
  • implementacja klienta i serwera w oparciu o stany oraz przydział zadań do poszczególnych klientów
  • wczytywanie plików oparte o stany (łatwość dodawania nowych elementów)

Więcej informacji i paczkę można znaleźć tutaj.

Operacje na systemie plików w systemach Windows i Linux w C++

Tym razem notka o tym, w jaki sposób można wykonywać różne operacje w systemie plików w wymienionych w temacie systemach. Oczywiście każdy programista C/C++ zna funkcje wykonujące podstawowe operacje na plikach, takie jak:

  • odczyt, modyfikacja, zapis (funkcja fopen i poboczne oraz klasy fstream)
  • zmiana nazwy pliku lub katalogu (funkcja rename())
  • usunięcie pliku (funkcja remove())

Nieco więcej problemów sprawiają pozostałe operacje, takie jak np. pobranie pełnej ścieżki do pliku czy przeglądanie listy plików w katalogu, ponieważ w bibliotece standardowej nie ma do tego celu odpowiednich funkcji, zatem należy ich szukać w API systemu. Niestety co API to różne funkcje, ale zdarzają się i takie, które istnieją w obu systemach.

Na początek pobieranie pełnej ścieżki do bieżącego katalogu, czyli funkcja getcwd. Jej prototyp w systemie Windows jest następujący:

#include <direct.h>
char* _getcwd(char* _DstBuf, int _SizeInBytes);

a w systemie Linux:

#include <unistd.h>
char* getcwd(char* _DstBuf, int _SizeInBytes);

Jak widać prototyp obu funkcji jest identyczny, więc nic nie stoi na przeszkodzie utworzenia sobie odpowiedniego aliasu nazwy za pomocą typedef.

Trochę lepiej jest z funkcją stat() i jej strukturą o tej samej nazwie (thx C…, typedef needed), która istnieje w obu systemach. Funkcja ta służy do pobierania pełnych informacji o pliku, czyli np. daty modyfikacji, typu czy rozmiaru. Jej przykładowe użycie wygląda następująco:

1
2
3
4
5
6
7
8
9
10
11
#include <sys/stat.h>
typedef struct stat FileStatus; // thx C
 
/*...*/
FileStatus fileStatus;
if(stat(szFullPath.c_str(), &fileStatus) < 0)
{
    return;
}
 
unsigned int uFileSize = fileStatus.st_size;	// Pobranie rozmiaru pliku

Nieco gorzej jest z wyciąganiem listy plików w katalogu, ponieważ w tym przypadku oba systemy mają do tego różne funkcje. W przypadku systemu Windows wygląda to tak:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <windows.h>
/*...*/
 
WIN32_FIND_DATA info;
HANDLE hFind = FindFirstFile(szDirPath, &info);
 
if(INVALID_HANDLE_VALUE == hFind)
{
    printf("Error");
    return;
}
 
do
{
    if((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
    {
        printf("Directory: %s\n", info.cFileName);
    }
    else
    {
        printf("File: %s\n", info.cFileName);
    }
}
while(FindNextFile(hFind, &info) != 0);
 
FindClose(hFind);

A w systemie Linux tak:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <dirent.h>
#include <string.h> // c-string operations
typedef struct dirent DirEntry;
typedef struct stat DirEntryStat; // thx C again
 
DIR* pDir;
pDir = opendir(szDirPath);
if(pDir == null)
{
    printf("Error");
    return;
}
 
DirEntry* pDirent;
DirEntryStat dirEntryStat;
while((pDirent = readdir(pDir)) != null)
{
    char buffer[255];
    strcpy(buffer, szDirPath);
    strcat(buffer, pDirent->d_name);	// Do ponizszej funkcji potrzebna jest sciezka z biezacego katalogu
 
    if(stat((buffer, &dirEntryStat) < 0)
    {
        printf("Error");
        continue;	// file corrupted... NEXT!
    }
 
    if((dirEntryStat.st_mode & S_IFDIR) != 0)	// Katalog
    {
        printf("Directory: %s\n", pDirent->d_name);
    }
    else if((dirEntryStat.st_mode & S_IFREG) != 0)	// Plik
    {
        printf("File: %s\n", info.pDirent->d_name);
    }
}
 
closedir(pDir);

Można zauważyć, że funkcja z WinAPI jest nieco przyjaźniejsza, ponieważ operuje na uchwycie do żądanego katalogu, w przeciwieństwie do funkcji Linuksowej, która po prostu zwraca nazwę tego co siedzi w katalogu i nic więcej.

Struktura katalogów w projekcie

Czasami istnieje potrzeba pisania jednego projektu w różnych środowiskach programistycznych, tym bardziej jeśli korzystamy z systemu kontroli wersji (np. SVN,  GIT) i chcemy skompilować projekt z założenia multiplatformowy na innym systemie operacyjnym. W takim przypadku fajnie by było, aby projekt miał strukturę katalogów niezależną od IDE, ale jak powinna ona wyglądać? Według mnie tak:

  • include
  • src
  • workdir
  • projects
  • libs

Katalog include powinien zawierać tylko pliki nagłówkowe, które będą dołączane w innych projektach. Tak naprawdę jest on tutaj tylko ze względu na projekt biblioteki (np. lib, dll, itp.), więc w przypadku kompilacji do pliku wykonywalnego, można ten katalog pominąć, wtedy wszystkie pliki nagłówkowe powinny znaleźć się w katalogu src. Katalog src jest przeznaczony tylko dla plików źródłowych bieżącego projektu, które mogą być uporządkowane w różnych podkatalogach. W workdir powinna się znaleźć cała struktura katalogów docelowego programu, czyli wszystkie pliki i katalogi, które będą dostarczane ze skompilowanym plikiem wykonywalnym. Katalog libs służy do przechowywania wszystkich dołączanych bibliotek używanych w aplikacji. Dla każdej z nich powinien znaleźć się odpowiedni katalog a w nim jeszcze dwa – include i lib. Ostatnim katalogiem jest projects, który jest przeznaczony do przechowywania plików projektów dla każdego IDE (oczywiście każdy w osobnym katalogu).

Taka struktura jest przejrzysta i niezależna od stosowanego IDE. Zresztą nie jest to mój pomysł, ponieważ jest on od dawna stosowany w środowiskach linuksowych, ale w nieco innej postaci. Kluczowe jest po prostu trzymanie plików źródłowych w jednym katalogu niezależnym od stosowanego środowiska.

Ubuntu – nowa zabawka

Któż by mógł przypuszczać, że Linux jest taką fajną zabawką. Okazuje się jednak, że ten system operacyjny może sprawić dużo frajdy. Ostatnio dużo czasu spędzam na Ubuntu i muszę stwierdzić, że jestem z niego zadowolony. Wybrałem tę dystrybucję, ponieważ jest bardzo prosta w obsłudze, a konkurencyjna Fedora miała problemy z instalacją. Sam system jest 64-bitowy, gdyż chciałem zobaczyć różnicę między nim, a wersją 32-bitową, którą miałem wcześniej. Wrażenie jest czysto subiektywne (wydaje się lepiej). Ze sterownikami nie było żadnych problemów, ponieważ Ubuntu zawiera wszystkie, jakie są potrzebne.

Wygląd Ubuntu można bardzo prosto dostosować, wystarczy tylko aby w systemie znalazł się dekorator okien Emerald oraz Compiz-Fusion. Dzięki temu tandemowi można Linuksa przerobić z wyglądu na Windowsa, a nawet jeszcze lepiej. Mój system wygląda jak na zdjęciu :).Wyglad

Inną rzeczą, która mnie w Linuksie zainsteresowała, to możliwość uruchamiania aplikacji windowsowych, szczególnie że udało mi się załapać na darmową wersję aplikacji CrossOver. Osobiście korzystam z dwóch wersji: Linux i Games, dlatego mogę przyznać, że są to bardzo dobre aplikacje.