Konsole: Optionen



  • Hi,
    folgende Frage:
    ich möchte in meiner Konsole eine Art Menü erstellen.
    Jedoch NICHT:

    1. Option 1
    2. Option 2
    3. Option 3
    
    Bitte Zahl eingeben:
    

    Sondern möchte ich einen farbigen Balken über die einzelnen Optionen bewegen lassen. Mit [Enter] wird dann die Option, auf der sich der Balken befindet ausgewählt.
    Siehe: http://www.computerhilfe-horch.de/images/award_main_big.jpg

    Gibt es dazu eine Art "Musterlösung" ?

    Mein (funktionierendes) Menü läuft zurzeit und diesem (sehr langen, aufwendigen) Muster:

    wenn Eingabe = 82: // Tastencode hoch
    -> clear Display, show Menü mit farbiger Unterlegung der oberen Option
    wenn Eingabe = 78: // Tastencode runter
    -> clear Display, show Menü mit farbiger Unterlegung der unteren Option
    

    Ich frage also die Tasteneingaben ab und arbeite mit IF-Anweisungen die verschienden Fälle ab.
    Funktioniert auch ohne Probleme, der Source Code ist nur sehr lang und verzweigt, da ich für das "Hauptmenü" verschiedene IFs brauche und wenn ich x-Untermenüs habe, brauche ich noch mal eine Menge IFs.

    Ich suche also einen recht einfachen Weg für ein solches beschriebenes Menü.

    Vielen Dank



  • Mach Dir eine Struktur:

    struct menuePunkt
    {
       string menuePunktText;
       int zeile;
       int spalte;
    };
    

    und verwalte davon soviele wie Du brauchst in einer Klasse. Dort merkst Du Dir immer den aktuell ausgewählten Menüpunkt.
    Wenn sich nun die Auswahl ändert (Pfeil hoch oder runter), färbst Du nur den alten Menüpunkt mit der 'normalen' Farbe und den neu ausgewählten mit der 'Auswahlfarbe'.
    Das kannst Du alles in der Verwaltungsklasse managen, so dass Du dann eine allgemeine, wiederverwendbare Lösung hast.

    ClearDisplay brauchst Du dann nur ein einziges Mal auszuführen, bevor das Menü erstmalig ausgegeben wird. Danach wie gesagt nur noch mit den Farben (WriteConsoleOutputAttribute) zu hantieren.



  • Ungefähr so könnte das dann aussehen:
    nützliche Konsolefunktionen:

    #ifndef CONSOLEFUNCTIONSH
    #define CONSOLEFUNCTIONSH
    
    #include <windows.h>
    #include <string>
    
    void clearScreen(WORD attribute = 7);
    void druckText(std::string text, int x, int y);
    void invertText(int x, int y, int len);
    
    #endif
    
    #include "consoleFunctions.h"
    
    void clearScreen(WORD attribute)
    {
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        COORD target = {0, 0};
        DWORD written;
    
        GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
        FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), ' ',
                                                csbi.dwSize.X * csbi.dwSize.Y,
                                                target, &written);
        FillConsoleOutputAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attribute,
                                                csbi.dwSize.X * csbi.dwSize.Y,
                                                target, &written);
    }
    
    void druckText(std::string text, int x, int y)
    {
        COORD target = {x, y};
        DWORD written;
    
        WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), text.c_str(),
                                                text.size(),
                                                target, &written);
    }
    
    void invertText(int x, int y, int len)
    {
        WORD attribut;
        DWORD dummy;
        COORD pos = {x, y};
    
        for(int i = 0; i < len; ++i)
        {
        	ReadConsoleOutputAttribute(GetStdHandle(STD_OUTPUT_HANDLE), &attribut, 1, pos, &dummy);
        	WORD tmpA = (attribut & 0x0F) << 4;
        	WORD tmpB = (attribut & 0xF0) >> 4;
        	WORD newAttribut = (attribut & 0xFF00) | tmpA | tmpB;
        	WriteConsoleOutputAttribute(GetStdHandle(STD_OUTPUT_HANDLE), &newAttribut, 1, pos, &dummy);
    		++pos.X;
    	 }	
    }
    

    die Menüklasse:

    #ifndef MENUE_H
    #define MENUE_H
    
    #include <windows.h>
    #include <string>
    #include <vector>
    
    class menue
    {
    	public:
    		void addToMenue(std::string text, int x, int y);
    		int loopMenue();
    		int startMenue();
    
    	private:
    
    		struct menueEintrag
    		{
    			std::string text;
    			int xPos;
    			int yPos;
    
    			menueEintrag(std::string t, int x, int y) : text(t), xPos(x), yPos(y){}
    		};
    
    		std::vector<menueEintrag> m;
    		int aktPos;
    };
    
    #endif
    
    #include <conio.h>
    #include "menue.h"
    #include "consoleFunctions.h"
    
    void menue::addToMenue(std::string text, int x, int y)
    {
    	m.push_back(menueEintrag(text, x, y));
    }
    
    int menue::loopMenue()
    {
    	char taste = getch();
    
    	while(taste != 13)
    	{
    		if(taste == 80)
    		{
    			invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    			++aktPos;
    			if(aktPos > m.size() - 1)
    				aktPos = 0;
    
    			invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    		}
    
    		if(taste == 72)
    		{
    			invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    			--aktPos;
    			if(aktPos < 0)
    				aktPos = m.size() - 1;
    
    			invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    		}
    
    		taste = getch();
    	}
    
    	return aktPos;
    }
    
    int menue::startMenue()
    {
    	if(!m.size())
    		return -1;   //Keine Menueeinträge
    
    	clearScreen();
    	COORD pos = {0, 0};
    	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
    
    	for(int i = 0; i < m.size(); ++i)
    		druckText(m[i].text, m[i].xPos, m[i].yPos);
    
    	invertText(m[0].xPos, m[0].yPos, m[0].text.size());
    	aktPos = 0;
    
    	return loopMenue();
    }
    

    und ein Testprogramm:

    #include <iostream>
    #include "menue.h"
    #include "consoleFunctions.h"
    
    int main()
    {
    	menue men;
    
    	men.addToMenue("Menuepunkt 1", 12, 5);
    	men.addToMenue("Menuepunkt 2", 12, 7);
    	men.addToMenue("Menuepunkt 3", 12, 9);
    	men.addToMenue("Menuepunkt 4", 12, 11);
    
    	int gewaehlt = men.startMenue();
    
    	clearScreen();
    	std::cout << "Gewaehlt wurde Menuepunkt " << gewaehlt;
    
    }
    


  • Vielen Dank für deinen sehr hilfreichen Beitrag!
    Du konntest mir wirklich sehr weiterhelfen.

    MfG



  • Ich habe die Menüklasse mal erweitert. Man kann nun den Menüpunkten einen Zeiger auf eine Funktion vom Typ

    void (*p)()
    

    mitgeben. Wenn man das tut, wird bei Auswahl des entsprechenden Menüpunktes nicht zurückgekehrt, sondern diese Funktion ausgeführt und danach wieder das Menü dargestellt.
    Auf diesem Weg kann man zum Beispiel relativ leicht Untermenüs erstellen:

    #ifndef MENUE_H
    #define MENUE_H
    
    #include <windows.h>
    #include <string>
    #include <vector>
    
    typedef void (*VerarbFunkPtr)();
    
    class menue
    {
    	public:
    		void addToMenue(std::string text, int x, int y, VerarbFunkPtr fPtr = 0);
    		int startMenue();
    
    	private:
    
    		struct menueEintrag
    		{
    			std::string text;
    			int xPos;
    			int yPos;
    			VerarbFunkPtr func;
    
    			menueEintrag(std::string t, int x, int y, VerarbFunkPtr ptr = 0) :
    			       text(t), xPos(x), yPos(y), func(ptr){}
    		};
    
    		int loopMenue();
    		void printMenue();
    
    		std::vector<menueEintrag> m;
    		int aktPos;
    };
    
    #endif
    
    #include <conio.h>
    #include "menue.h"
    #include "consoleFunctions.h"
    
    void menue::addToMenue(std::string text, int x, int y, VerarbFunkPtr fPtr)
    {
    	m.push_back(menueEintrag(text, x, y, fPtr));
    }
    
    int menue::startMenue()
    {
    	if(!m.size())
    		return -1;   //Keine Menueeinträge
    
    	aktPos = 0;
    
    	printMenue();	
    	return loopMenue();
    }
    
    int menue::loopMenue()
    {
    	char taste = getch();
    
    	while(true)
    	{
    		if(taste == 80)
    		{
    			invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    			++aktPos;
    			if(aktPos > m.size() - 1)
    				aktPos = 0;
    
    			invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    		}
    
    		if(taste == 72)
    		{
    			invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    			--aktPos;
    			if(aktPos < 0)
    				aktPos = m.size() - 1;
    
    			invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    		}
    
    		if(taste == 13)
    		{
    			if(m[aktPos].func)
    			{
    				m[aktPos].func();
    				printMenue();
    			}
    			else
    			{
    				return aktPos;	
    			}
    		}
    
    		taste = getch();
    	}
    }
    
    void menue::printMenue()
    {
    	clearScreen();
    	COORD pos = {0, 0};
    	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
    
    	for(int i = 0; i < m.size(); ++i)
    		druckText(m[i].text, m[i].xPos, m[i].yPos);
    
    	invertText(m[aktPos].xPos, m[aktPos].yPos, m[aktPos].text.size());
    }
    

    Ein Testprogramm:

    #include <iostream>
    #include <conio.h>
    #include "menue.h"
    #include "consoleFunctions.h"
    
    void verarbMen3()
    {
    	menue men;
    	men.addToMenue("Untermenuepunkt Eins", 12, 7);
    	men.addToMenue("Untermenuepunkt Zwei", 12, 11);
    
    	int gewaehlt = men.startMenue();
    	clearScreen();
    	std::cout << "im Untermenue wurde Punkt" << gewaehlt << " ausgewaehlt\n";
    	getch();
    }
    
    int main()
    {
    	menue men;
    
    	men.addToMenue("Menuepunkt 1", 12, 5);
    	men.addToMenue("Menuepunkt 2", 12, 7);
    	men.addToMenue("Menuepunkt 3", 12, 9, verarbMen3);
    	men.addToMenue("Menuepunkt 4", 12, 11);
    
    	int gewaehlt = men.startMenue();
    
    	clearScreen();
    	std::cout << "Gewaehlt wurde Menuepunkt " << gewaehlt;
    
    }
    

Anmelden zum Antworten