Ir ao conteúdo

Posts recomendados

Postado

Como exibir um submenu após o usuário selecionar uma opção usando ncurses?

 

mvwprintw(win, 1,2,"Selecione uma opção:");
mvwprintw(win,3,2,"Cliente");
mvwprintw(win,4,2,"Funcionário");
mvwprintw(win,7,2,"Sair");
wrefresh(win);

while (!sair){
	mvwprintw(win,opcao,0,"->");
	wrefresh(win);

	ch = getch();
	switch(ch){
		case KEY_DOWN:
			if (opcao != 7){
				mvwprintw(win,opcao,0,"  ");
				opcao++;
			}else{
				mvwprintw(win,opcao,0,"  ");
				opcao = 3;
			}
			break;
		case KEY_UP:
			if (opcao != 3){
				mvwprintw(win,opcao,0,"  ");
				opcao--;
			}else{
				mvwprintw(win,opcao,0,"  ");
				opcao = 7;
			}
			break;
		case '\n':
			if(opcao == 3){
				mvprintw(1,max_x/2,"Cliente");
				mvwprintw(win,1,2,"Selecione uma opção:");
				mvwprintw(win,3,2,"Comprar Ingressos");
				mvwprintw(win,4,2,"Consultar Filmes em Cartaz");
				mvwprintw(win,5,2,"Checar ingresso");
				mvwprintw(win,7,2,"Sair");
				wrefresh(win);
			/*se o usuario selecionar 'comprar ingressos' deve aparecer esse submenu:
					mvprintw(1,max_x/2,"Cliente");
					mvwprintw(win,1,2,"Selecione uma opção:");
					mvwprintw(win,3,2,"FILME 1");
					mvwprintw(win,4,2,"FILME 2");
					mvwprintw(win,5,2,"FILME 3");
					mvwprintw(win,7,2,"Sair");
					wrefresh(win);
			*/
			}
			else if(opcao == 4){
				mvprintw(1,max_x/2,"Funcionário");
				mvwprintw(win,1,2,"Selecione uma opção:");
				mvwprintw(win,3,2,"Cadastrar Filme");
				mvwprintw(win,4,2,"Remover Filme");
				mvwprintw(win,5,2,"Visualizar Salas");
				mvwprintw(win,7,2,"Sair");
				wrefresh(win);
			}
			else{
				sair = 1;
			}
	}

 

Postado

@arfneto

#include<stdio.h>
#include<ncurses.h>
#include<unistd.h>
#include<string.h>

#define NLINHA 8
#define NCOL 50

int main()
{
	initscr(); 
	noecho();
	curs_set(FALSE);
	keypad(stdscr, TRUE);
	
	int max_y, max_x, y, x, ch;
	int opcao = 3, sair = 0;
	char salas[8][10];

	getmaxyx(stdscr,max_y,max_x);
	WINDOW * win = newwin(NLINHA,max_x/2-1,1,1);
	keypad(win, TRUE);
	refresh();
	
	for(x = 0; x <= max_x; x++){
		mvprintw(0, x, "-");
		mvprintw(NLINHA+2, x, "-");
	}
	
	mvprintw(1,max_x/2,"Cinema");
	mvwprintw(win, 1,2,"Selecione uma opção:");
	mvwprintw(win,3,2,"Cliente");
	mvwprintw(win,4,2,"Funcionário");
	mvwprintw(win,7,2,"Sair");
	wrefresh(win);

	while (!sair){
		mvwprintw(win,opcao,0,"->");
		wrefresh(win);

		ch = getch();
		switch(ch){
			case KEY_DOWN:
				if (opcao != 7){
					mvwprintw(win,opcao,0,"  ");
					opcao++;
				}else{
					mvwprintw(win,opcao,0,"  ");
					opcao = 3;
				}
				break;
			case KEY_UP:
				if (opcao != 3){
					mvwprintw(win,opcao,0,"  ");
					opcao--;
				}else{
					mvwprintw(win,opcao,0,"  ");
					opcao = 7;
				}
				break;
			case '\n':
				if(opcao == 3){
					mvprintw(1,max_x/2,"Cinema - Cliente");
					mvwprintw(win,1,2,"Selecione uma opção:");
					mvwprintw(win,3,2,"Comprar Ingressos");
					mvwprintw(win,4,2,"Consultar Filmes em Cartaz");
					mvwprintw(win,5,2,"Checar ingresso");
					mvwprintw(win,7,2,"Sair");
					wrefresh(win);
                  		/*se o usuario selecionar 'comprar ingressos' deve aparecer esse submenu:
					mvprintw(1,max_x/2,"Cliente");
					mvwprintw(win,1,2,"Selecione uma opção:");
					mvwprintw(win,3,2,"FILME 1");
					mvwprintw(win,4,2,"FILME 2");
					mvwprintw(win,5,2,"FILME 3");
					mvwprintw(win,7,2,"Sair");
					wrefresh(win);
				*/	
				}
				else if(opcao == 4){
					mvprintw(1,max_x/2,"Cinema - Funcionário");
					mvwprintw(win,1,2,"Selecione uma opção:");
					mvwprintw(win,3,2,"Cadastrar Filme");
					mvwprintw(win,4,2,"Remover Filme");
					mvwprintw(win,5,2,"Visualizar Salas");
					mvwprintw(win,7,2,"Sair");
					wrefresh(win);
				}
				else{
					sair = 1;
				}
		}
	}
	delwin(win);
 	endwin();
	return 0;
}

 

Postado

Olá @Elize Yakamura

 

Do modo como está fazendo isso vai dar muito trabalho. Sugiro repensar.

 

ncurses, derivada de curses --- é um toolbox, uma caixa de ferramentas para controlar a tela. Não é um framework para escrever interfaces.

 

Isso quer dizer que ncurses tem as abstrações de tela, janela, painéis e coisas assim, mas o plano geral é por conta do programador. Com ncurses você cria as funções que controlam a interface. ncurses não controla a interface. Esse nível você tem que escrever.

 

Para mostrar menus e submenus o mais simples é usar uma janela para cada um, porque você pode tratar as janelas como unidade e dar um fim nelas, mover, apagar e tal. E cria funções que fazem isso e uma estrutura de suporte, esse outro nível deque falei.

 

ncurses.h, menu.h e história

 

Com a migração de tudo para a nuvem ressurgiu nos últimos anos o interesse por esse tipo de coisa "anos 80". A Microsoft por exemplo investiu e está investindo milhões nessas coisas, o tal modo texto. Isso porque muitos serviços na nuvem não tem interface gráfica e precisam de configuração.

 

curses é dos anos 80. ncurses dos anos 90. E resolvia --- resolve --- muitos problemas. Naquela época getopt() em C e curses eram um divisor para o cidadão que escrevia aplicações. Quem conhecia programava em outro patamar. getopt() tratava da entrada e curses da tela e teclado.

 

Nem todo computador tinha terminais com tela. E poucos terminais tinham cor. O computador tinha vários terminais, cada um com seu teclado e ponto de força. Não havia e não há a noção de tela no terminal --- xterm. curses trazia isso.

 

De volta ao programa

 

ncurses tem suporte a menus, e bem sofisticado, com multi-select, opções com descrição, defaults, telcas de função, edição, cores e tudo mais.

Citação

Não deve escrever na mão tudo isso. Pode parecer mais simples, mas não é. Vai estar reinventando o que está em menu.h e o cara que escreveu aquilo era muito bom.

 

Sei que é difícil encontrar exemplos disso, ou acho que é porque procurei e não achei quase nada ontem. Vou te mostrar um exemplo em C uma sugestão de como continuar. Pode claro usar ncurses em C++ e outras linguagens, como Python e javascript.

 

image.png.fea7c38cb51961b1ac009fac64633708.png

 

O programa é bem tosco, apenas um exemplo comentado para você entender a mecânica. E do modo como eu faria. Nem vou dizer que é o certo ou que é uma boa maneira.

 

Mas eu já escrevi um troço desses em assembler para uma plataforma proprietária e era bem usado desse modo, então tem boa chance de servir

 

Aqui à esquerda está a tela: na tela original tem apenas o título, optei por deixar só a mensagem com a versão de curses que tinha na máquina que eu estava usando, Ubuntu 20 rodando em Windows. Eu procuro não acessar máquinas Linux aqui para não mudar de contexto quando acesso o forum.

 

 

O menu é o do seu programa e não faz diferença. ncurses tem scroll e multi-select e pode até tratar mais de um terminal ao mesmo tempo ou tratar uma janela maior que a tela.
 

image.png.9c828d0d4d87c4e9664bb7a040956f93.png

Ao sair do menu o programa volta para a tela original e apaga a janela. Isso é o que eu acho mais simples e o que eu passava pro meu pessoal

 

Tecle 'x' para encerrar é pobre mas ;) estou sem inspiração.

 

Entenda o uso de um arquivo LOG aqui

 

#define ENTER '\n'
#define LOG "./ncurses.log.txt"

int main(void)
{
    char *opcao_0[] = {"Cliente", "Funcionario", "Sair", NULL};

    // abre o log
    FILE *log = fopen(LOG, "a+");  // append
    if (log == NULL) return -1;

 

Pra que isso? Nesses programas é um porre seguir a execução e escrever mensagens e ver o que está acontecendo, porque tudo é escrito em termos de linha e coluna. Assim um arquivo de texto onde você vai escrevendo o que precisa é bem legal: você pode ir vendo o arquivo em tempo real --- tail -f em outra tela era o normal, e sem mexer nas janelas do programa...

 

Note em opcao_0[] uma maneira simples de juntas as opções do menu. E isso você deveria ter usado já no seu programa ao invés daqueles 

 

        mvwprintw(win, 3, 2, "Cadastrar Filme");
        mvwprintw(win, 4, 2, "Remover Filme");
        mvwprintw(win, 5, 2, "Visualizar Salas");
        mvwprintw(win, 7, 2, "Sair");

 

mvwprintw() soltos que dão um trabalho do  inferno.

 

Exemplo do LOG

 

Nesse caso o cara entrou no programa e foi até a opção Sair e teclou ENTER. É só para você entender a razão. 

 

versão: ncurses 6.2.20200212
(linhas) y = 30, (colunas) x = 120
Sim, terminal tem cores: 256 pares
set_menu_opts() retornou OK
scale_menu() retornou 3x14
3 itens reportados para esse menu
O menu retornou 2
Saindo com opcao 2

 

Um exemplo sério de fflush()

 

Incontáveis vezes aqui tem programas usando --- e usuários recomendando --- o fflush(stdin) e o tal setbuf() para controlar o buffer do teclado (!) e então aqui tem um exemplo de quando você precisa de flush() com uma razão séria num arquivo de saída.

 

Porque? Porque depois de gravar algo no tal arquivo log o programa pode cancelar nas linhas seguintes e é preciso garantir que a mensagem seja gravada no disco. Ou você não vai saber onde o programa parou...

 

    scale_menu(menu_0, &y_0, &x_0);
    fprintf(log, "scale_menu() retornou %ux%u\n", y_0, x_0);
    fprintf(
        log, "%d itens reportados para esse menu\n",
        item_count(menu_0));
    fflush(log);

 

Assim o flush garante que os dados serão gravados naquele ponto do programa.

 

A lógica do exemplo

 

A ideia é criar uma janela com uma borda para facilitar a identificação. A janela tem o tamanho calculado para, claro, envolver as opções do menu. E tem uma linha de título e uma linha em branco antes das opções. A linha em branco em geral era uma barra horizontal na linha 2. Então o menu começa na linha 3... E assim o usuário logo se acostuma com a cara do menu:
 

/------------\
|   titulo   |
|------------|
|   opcao 1  |
|   opcao 1  |
|   opcao 1  |
|   opcao X  |
\------------/

 

E a lógica é essa: cria o menu, calcula e mostra a janela. Apaga a janela e a vida segue, retornando a opção.

 

O código completo do exemplo

 

#include <menu.h>
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ENTER '\n'
#define LOG "./ncurses.log.txt"

int main(void)
{
    char *opcao_0[] = {"Cliente", "Funcionario", "Sair", NULL};

    // abre o log
    FILE *log = fopen(LOG, "a+");  // append
    if (log == NULL) return -1;

    initscr();  // basico
    start_color();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);

    init_pair(1, COLOR_YELLOW, COLOR_BLACK);

    fprintf(log, "versão: %s\n", curses_version());
    int x, y;  // colunas e linhas da tela ao abrir o programa
    getmaxyx(stdscr, y, x);
    fprintf(log, "(linhas) y = %d, (colunas) x = %d\n", y, x);

    if (has_colors())
        fprintf(
            log, "Sim, terminal tem cores: %d pares\n", COLOR_PAIRS);
    else
        fprintf(log, "não, o terminal não tem cores\n");

    fflush(log);
    clear();
	char versão[20]={0}; // para mostrar ncurses x.y
    wattron(stdscr, COLOR_PAIR(1));
    mvwprintw(stdscr, 1, 3, "%s", strncpy(versão,curses_version(),11));
    wattroff(stdscr, COLOR_PAIR(1));
    refresh();

    // cria o vetor de opções
    size_t n_opcao_0 = sizeof(opcao_0) / sizeof(opcao_0[0]);
    ITEM **item_0    = (ITEM **)calloc(n_opcao_0, sizeof(ITEM *));
    for (size_t i = 0; i < n_opcao_0; i += 1)
        item_0[i] = new_item(opcao_0[i], "");

    MENU *menu_0 = new_menu(item_0);  // o menu
    set_menu_mark(menu_0, " > ");
    // define opções para o nivel
    Menu_Options opt_0 =
        ~O_ONEVALUE | ~O_SHOWDESC | O_IGNORECASE | O_NONCYCLIC;
    int n = set_menu_opts(menu_0, opt_0);
    if (n == E_OK)
        fprintf(log, "set_menu_opts() retornou OK\n");
    else
        fprintf(log, "set_menu_opts() retornou %d\n", n);
    fflush(log);

    int y_0 = 0, x_0 = 0;  // linhas e colunas para caber o menu
    scale_menu(menu_0, &y_0, &x_0);
    fprintf(log, "scale_menu() retornou %ux%u\n", y_0, x_0);
    fprintf(
        log, "%d itens reportados para esse menu\n",
        item_count(menu_0));
    fflush(log);

    // cria uma janela para o menu
	// scale_menu() ja deu o tamanho minimo
    // uma linha para opcao, mais as bordas e o titulo
    // colunas - largura da maior opcao mais largura do menu_mark()
    // mais as bordas
    WINDOW *Win_0 = newwin(
        (y_0 + 4), (x_0 + 2 + strlen(menu_mark(menu_0))), 10, 10);
    box(Win_0, 0, 0);
    wrefresh(Win_0);

    set_menu_win(menu_0, Win_0); // associa o menu e a janela
    set_menu_sub(menu_0, derwin(Win_0, y_0, x_0, 3, 1));
    keypad(Win_0, TRUE);
    post_menu(menu_0); // mostra o menu afinal
    wrefresh(Win_0);

    int c       = 0;
    int count_0 = item_count(menu_0);  // para usar no loop
    int dentro = 1;
    while (dentro)
    {
        int cmd = 0; // comando para o menu
        c = wgetch(Win_0);
        n = item_index(current_item(menu_0));
        switch (c)
		{
            case KEY_DOWN:
              cmd = (n == count_0 - 1) ? REQ_FIRST_ITEM : REQ_DOWN_ITEM;
              menu_driver(menu_0, cmd);
              break;

            case KEY_UP:
              cmd = (n == 0) ? REQ_LAST_ITEM : REQ_UP_ITEM;
              menu_driver(menu_0, cmd);
              break;

            case ENTER: // '\n'
                fprintf(log, "O menu retornou %d\n", n);
                fflush(log);
                unpost_menu(menu_0);
                free_menu(menu_0);
                dentro = 0;
                break;

            default:
                break;
        };  // switch()
        //wrefresh(Win_0);

    };  // while()

	// fechas as coisas
    wclear(Win_0);
    wrefresh(Win_0);
    refresh();

    wattron(stdscr, COLOR_PAIR(1));
    mvwprintw(stdscr, 2, 3, "Menu nivel 0 retornou %d\n", n);
    mvwprintw(stdscr, 3, 3, "Tecle 'x' para encerrar\n");
    wattroff(stdscr, COLOR_PAIR(1));
    refresh();

    while ((c = wgetch(stdscr)) != 'x');  // loop !!!!!!!!!!!!!!!!!!

    fprintf(log, "Saindo com opcao %d\n", n);
    fflush(log);

    // libera os items
    for (size_t i = 0; i < n_opcao_0; i += 1) free_item(item_0[i]);
    endwin(); // fecha tudo
    return 0;
}

 

Acho que vai entender. É bem linear.

 

Um programa profissional usaria algo assim:
 

    typedef struct 
    {
        size_t      id;
        ITEM**      item; // items
        MENU*       menu; // menu
        size_t      n_opt; // quantas
        Menu_Options opt; // opções especiais
        const char* tit; // titulo
        WINDOW*     win; // janela

    }   Menu_desc;

 

Porque aí pode colocar toda a descrição do menu DENTRO da estrutura --- encapsulamento. E pode escrever funções como

 

	int   o_menu(Menu_desc* menu);

 

que mostra o menu, espera a opção, apaga a janela e retorna para o programa ;) . Em C++ usaria uma classe, claro.

 

E usar um array com todos os menus do programa, tipo

 

    Menu_desc     meu-menu[20]; // 20 menus no programa
    Menu_desc**   todos_menus = NULL; // e constroi um vetor de menus 


Para compilar o exemplo use 
 

    gcc -o teste -Wall -std=c17 teste.c -lmenu -lncurses

 

ou algo assim.

 

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

LANÇAMENTO!

eletronica2025-popup.jpg


CLIQUE AQUI E BAIXE AGORA MESMO!