Grafische Oberfläche. Aber wie?



  • @EinNutzer0 sagte in Grafische Oberfläche. Aber wie?:

    Ich zeige euch mal, wie das im Visual Studio aussieht (ein Skeleton):

    // WindowsProject1ED.cpp : Definiert den Einstiegspunkt für die Anwendung.
    //
    
    #include "framework.h"
    #include "WindowsProject1ED.h"
    
    #define MAX_LOADSTRING 100
    
    // Globale Variablen:
    HINSTANCE hInst;                                // Aktuelle Instanz
    WCHAR szTitle[MAX_LOADSTRING];                  // Titelleistentext
    WCHAR szWindowClass[MAX_LOADSTRING];            // Der Klassenname des Hauptfensters.
    
    // Vorwärtsdeklarationen der in diesem Codemodul enthaltenen Funktionen:
    ATOM                MyRegisterClass(HINSTANCE hInstance);
    BOOL                InitInstance(HINSTANCE, int);
    LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
    INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE hPrevInstance,
                         _In_ LPWSTR    lpCmdLine,
                         _In_ int       nCmdShow)
    {
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
    
        // TODO: Hier Code einfügen.
    
        // Globale Zeichenfolgen initialisieren
        LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadStringW(hInstance, IDC_WINDOWSPROJECT1ED, szWindowClass, MAX_LOADSTRING);
        MyRegisterClass(hInstance);
    
        // Anwendungsinitialisierung ausführen:
        if (!InitInstance (hInstance, nCmdShow))
        {
            return FALSE;
        }
    
        HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1ED));
    
        MSG msg;
    
        // Hauptnachrichtenschleife:
        while (GetMessage(&msg, nullptr, 0, 0))
        {
            if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    
        return (int) msg.wParam;
    }
    
    
    
    //
    //  FUNKTION: MyRegisterClass()
    //
    //  ZWECK: Registriert die Fensterklasse.
    //
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
        WNDCLASSEXW wcex;
    
        wcex.cbSize = sizeof(WNDCLASSEX);
    
        wcex.style          = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc    = WndProc;
        wcex.cbClsExtra     = 0;
        wcex.cbWndExtra     = 0;
        wcex.hInstance      = hInstance;
        wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1ED));
        wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
        wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1ED);
        wcex.lpszClassName  = szWindowClass;
        wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
        return RegisterClassExW(&wcex);
    }
    
    //
    //   FUNKTION: InitInstance(HINSTANCE, int)
    //
    //   ZWECK: Speichert das Instanzenhandle und erstellt das Hauptfenster.
    //
    //   KOMMENTARE:
    //
    //        In dieser Funktion wird das Instanzenhandle in einer globalen Variablen gespeichert, und das
    //        Hauptprogrammfenster wird erstellt und angezeigt.
    //
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
       hInst = hInstance; // Instanzenhandle in der globalen Variablen speichern
    
       HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
          CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    
       if (!hWnd)
       {
          return FALSE;
       }
    
       ShowWindow(hWnd, nCmdShow);
       UpdateWindow(hWnd);
    
       return TRUE;
    }
    
    //
    //  FUNKTION: WndProc(HWND, UINT, WPARAM, LPARAM)
    //
    //  ZWECK: Verarbeitet Meldungen für das Hauptfenster.
    //
    //  WM_COMMAND  - Verarbeiten des Anwendungsmenüs
    //  WM_PAINT    - Darstellen des Hauptfensters
    //  WM_DESTROY  - Ausgeben einer Beendenmeldung und zurückkehren
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_COMMAND:
            {
                int wmId = LOWORD(wParam);
                // Menüauswahl analysieren:
                switch (wmId)
                {
                case IDM_ABOUT:
                    DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                    break;
                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
                }
            }
            break;
        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hWnd, &ps);
                // TODO: Zeichencode, der hdc verwendet, hier einfügen...
                EndPaint(hWnd, &ps);
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    // Meldungshandler für Infofeld.
    INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
        UNREFERENCED_PARAMETER(lParam);
        switch (message)
        {
        case WM_INITDIALOG:
            return (INT_PTR)TRUE;
    
        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, LOWORD(wParam));
                return (INT_PTR)TRUE;
            }
            break;
        }
        return (INT_PTR)FALSE;
    }
    

    bei mir sieht das so aus:

    #include <Windows.h>
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    {
    	static TCHAR szAppName[] = TEXT("Aufgäbe 1");
    	HWND hwnd;
    	MSG msg;
    	WNDCLASS wndclass;
    
    	wndclass.style = CS_HREDRAW | CS_VREDRAW;
    	wndclass.lpfnWndProc = WndProc;
    	wndclass.cbClsExtra = 0;
    	wndclass.cbWndExtra = 0;
    	wndclass.hInstance = hInstance;
    	wndclass.hIcon = LoadIcon(0, IDI_APPLICATION);
    	wndclass.hCursor = LoadCursor(0, IDC_HAND);
    	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    	wndclass.lpszMenuName = 0;
    	wndclass.lpszClassName = szAppName;
    	
    
    	if (!RegisterClass(&wndclass))
    	{
    		MessageBox(0, TEXT("Fehler!"), szAppName, MB_ICONERROR);
    		return 0;
    	}
    
    	hwnd = CreateWindow(szAppName, TEXT("Aufgabe 1"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0);
    
    	ShowWindow(hwnd, iCmdShow);
    	UpdateWindow(hwnd);
    
    	while (GetMessage(&msg, 0, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	HDC hdc;
    	PAINTSTRUCT ps;
    	RECT rect;	
    
    	switch (message) 
    	{
    	case WM_CREATE:
    		return 0;
    	case WM_PAINT:
    		hdc = BeginPaint(hwnd, &ps);
    
    		SetTextColor(hdc, 0x00FF00FF);
    
    		GetClientRect(hwnd, &rect);
    
    		DrawText(hdc, TEXT("Hallo Welt!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    
    		EndPaint(hwnd, &ps);
    		return 0;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	}
    
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    

    Ich brauche n halbes Jahr, um das zu verstehen...
    Ich wette, das ist absichtlich so kompliziert, damit die Leute nach C# switchen.

    xcb wäre auch nicht viel anders, aber ich habe "damals" vielleicht 30 min gebraucht, um das zu verstehen. 🙄


  • Gesperrt

    @Wade1234 Hm, das klingt schlüssig...
    Aber was wäre denn jetzt ein "geeignetes Vorgehen" für einen "Anfänger"?
    Darf ich Deinen Code verwenden?

    Dass ich erst einen XServer mit Terminal und dann das Programm starten muss, möchte ich eigentlich nicht. Wie gesagt, die "Kernlogik" meines Programms ist in C++ geschrieben. Mir fehlt eben noch die grafische Ausgabe.



  • @EinNutzer0 sagte in Grafische Oberfläche. Aber wie?:

    @Wade1234 Hm, das klingt schlüssig...
    Aber was wäre denn jetzt ein "geeignetes Vorgehen" für einen "Anfänger"?
    Darf ich Deinen Code verwenden?

    Dass ich erst einen XServer mit Terminal und dann das Programm starten muss, möchte ich eigentlich nicht. Wie gesagt, die "Kernlogik" meines Programms ist in C++ geschrieben. Mir fehlt eben noch die grafische Ausgabe.

    Habe den Thread nur überflogen - was sprach jetzt gegen ein GUI-Framework wie Qt? Wenn dir die direkte GUI-Programmierung zu kompliziert und zu wenig portabel ist, dann ist eine Bibliothek, die dir das für verschiedene Desktop/Grafik-Systeme (Win32, X-Server, EGL oder Wayland) abnimmt, eigentlich eine natürliche Wahl.

    Qt hat mit Qt Creator auch eine gar nicht mal so schlechte IDE, wenn du schon schreibst, dass du "ohne das VS entwickeln" möchtest. Damit könntest du dann u.a. auch den GCC oder Clang mit der MinGW-Runtime verwenden und Windows-Software ohne MS-Compiler/IDE entwickeln (MinGW-Compiler separat herunterladen und installieren).

    CLI-Zeug erachte ich auch als unproblematisch mit Qt. Ist zwar etwas länger her bei mir, ich bin mir aber ziemlich sicher, dass es kein Problem ist, Kommandozeilen-Argumente zu verarbeiten und GUI-Fenster auch nur bei Bedarf aufzumachen.

    Einarbeitungszeit brauchst du aber für alle diese Lösungen - von direkter Win32/Xlib-Programmierung würde ich aber dringend abraten, es sei denn du brauchst nicht mehr ein simples Fenster: Nicht portabel, umständlich und antike APIs.

    Es gibt viele Lösungsansätze für dein Problem. Meine persönliche Empfehlung wäre: Das von @5cript empfohlene MSYS2 mit deren Version des GCC mit MinGW-w64-Runtime (mingw64) und Qt mit dem Qt Creator als IDE in Open Source-Variante mit LGPL-Lizenz (kannst du auch für Software verwenden, die du unter eigener Lizenz verkaufen willst, solange du die LGPL-Bedingungen einhältst - dynamisch linken ist da die einfachste Variante).



  • @EinNutzer0 sagte in Grafische Oberfläche. Aber wie?:

    • "Fenster", schließen, minimieren, maximieren, etc.,
    • Textfeld, für die Ausgabe von Plaintext und die manuelle Änderung von Plaintext,
    • Fortschrittsbalken, die Werte 0 bis 100 sollten anzeigt werden können,
    • 5 Buttons, wenn man einen Button anklickt, dann soll das Programm etwas tun,

    Echt jetzt? Wenn das Ding nie portiert werden soll und nicht größer wird WinAPI.

    @EinNutzer0 sagte in Grafische Oberfläche. Aber wie?:

    • ein "Programmdesign", das MVC-Pattern wäre wünschenswert, also die Aufteilung in Daten, Oberfläche, Listenern und Logik...

    Kannst Du ja machen wie Du lustig bist.



  • Also von WinAPI würde ich ja abraten (insbesondere bei C++).

    Aber was spricht jetzt gegen Nana? Das Makro per -DWIN32 zu den Projektoptionen hinzu zufügen dürfte ja jetzt nicht so schwer sein...



  • @EinNutzer0 ja den code darfst du verwenden. es gibt auch ein buch namens windows programmierung von charles petzold, welches dir die wichtigsten teile der windows api erklärt. allerdings müsstest du dann zumindest teilweise auf komfort wie die stl verzichten.



  • @Wade1234 sagte in Grafische Oberfläche. Aber wie?:

    allerdings müsstest du dann zumindest teilweise auf komfort wie die stl verzichten.

    So ein Blödsinn.



  • @Th69 sagte in Grafische Oberfläche. Aber wie?:

    Also von WinAPI würde ich ja abraten (insbesondere bei C++).

    Ich auch. Ich stehe nicht besonders auf Selbstkasteiung. In der heutigen Zeit ist die veraltete Windows API ein Krampf. Es gibt eigentlich genug Frameworks ( z.B. QT ), die das wrappen und es so zumindest etwas erträglicher machen. Das QT-Framework ist allerdings auch nicht gerade der neueste C++-Standard, aber besser als die winapi...



  • @Th69 sagte in Grafische Oberfläche. Aber wie?:

    Also von WinAPI würde ich ja abraten (insbesondere bei C++).

    von ms gibt es eine c++ klassenbibliothek für winapi. die ist bestimmt schon 30 jahre alt, aber bei den neuen versionen von visual c++ immer noch dabei.


  • Gesperrt

    @Th69 sagte in Grafische Oberfläche. Aber wie?:

    Also von WinAPI würde ich ja abraten (insbesondere bei C++).

    Aber was spricht jetzt gegen Nana? Das Makro per -DWIN32 zu den Projektoptionen hinzu zufügen dürfte ja jetzt nicht so schwer sein...

    Geht nicht, ich bekomme Nana mit cygwin64 nicht compiliert.

    c++defines.hpp :

    // Select platform  ......
    #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || 1	//Microsoft Windows
    	#define NANA_WINDOWS
    	typedef unsigned long thread_t;
    
    	// MINGW ...
    	#if defined(__MINGW32__) || defined(__MINGW64__) || defined(MINGW) || 1
    		#define NANA_MINGW
    	#endif // MINGW
    
    #elif defined(APPLE)	//Mac OS X
    

    (zweimal 1 hinzugefügt (s. d. er NANA_WINDOWS und NANA_MINGW setzt)..., dann cmake und make...)

    Fehlermeldung :

    [ 15%] [ 15%] Building CXX object CMakeFiles/nana.dir/source/detail/posix/theme.cpp.o
    [ 16%] [ 16%] Building CXX object CMakeFiles/nana.dir/source/filesystem/filesystem.cpp.o
     /nana/source/filesystem/filesystem.cpp: In Funktion »bool nana::filesystem_ext::modified_file_time(const std::experimental::filesystem::v1::path&, tm&)«:
     /nana/source/filesystem/filesystem.cpp:119:69: Fehler: »const value_type* {aka const char*}« kann nicht nach »LPCWSTR {aka const wchar_t*}« für Argument »1« nach »WINBOOL GetFileAttributesExW(LPCWSTR, GET_FILEEX_INFO_LEVELS, LPVOID)« umgewandelt werden
        if (::GetFileAttributesEx(p.c_str(), GetFileExInfoStandard, &attr))
    


  • Setz die CMake-Variable NANA_CMAKE_NANA_FILESYSTEM_FORCE auf ON, dann könnte es funktionieren.


  • Gesperrt

    @Hubert-Alfonsius , Danke, wieder einen Schritt weiter:

    set(NANA_CMAKE_NANA_FILESYSTEM_FORCE "ON"):

    
    [ 20%] Building CXX object CMakeFiles/nana.dir/source/gui/dragdrop.cpp.o
    In file included from /usr/include/w32api/comdef.h:182:0,
                     from /nana/source/gui/dragdrop.cpp:27:
    /usr/include/w32api/comip.h:21:10: schwerwiegender Fehler: new.h: No such file or directory
     #include <new.h>
              ^~~~~~~
    Kompilierung beendet.
    
    


  • Das hier ist mein workflow wenn ich neues projekt mit nana anfange:
    (mit msys2)

    git clone https://github.com/cnjinhao/nana.git
    cd nana
    git checkout develop
    
    # build ordner leider schon vorhanden
    mkdir release
    cd release
    cmake -DCMAKE_CXX_STANDARD=17 -DNANA_CMAKE_ENABLE_PNG=ON -DNANA_CMAKE_ENABLE_JPEG=ON -G"MSYS Makefiles" -DNANA_CMAKE_ENABLE_AUDIO=ON ..
    make -j24
    

Anmelden zum Antworten