Jump to content

dbslibmenu - recurso para criação de menus


dardevelin
 Share

Recommended Posts

Boas, depois de ter algumas ideias para algumas aplicações que necessitavam menus e de ver alguns pedidos frequentes de ajuda sobre os mesmos, decidi fazer um pequeno conjunto de funções em C, organizados na forma de biblioteca para facilitar a criação do ditos menus. Isto tudo porque as vezes, queremos/precisamos de interacção para o próprio teste da aplicação e então la vamos nós mais uma vez fazer um menu, antes daquilo que realmente interessa.

como publicar no geshi não é a melhor forma para disponibilizar vários ficheiros, deixo aqui o link para o repositório, e um snippet de um demo, do como usar esta mini lib.

https://github.com/dardevelin/dbslibmenu

Notas a ter em atenção:

  • A biblioteca é recente, logo pouco testada bugs -> reporte agradecido
  • Podia ter sido desenhada de uma forma mais simplificada. Foi uma decisão de design.
  • A biblioteca esta susceptível a mudanças na aplicação da interface, se pretendem usar a mesma em código de produção e ou precisem de manter alguma compatibilidade, falem comigo, saber o que usam e como usam permite fazer um desenvolvimento mais orientado aos utilizadores.
  • Ainda não foram criados os mecanismos para configuração de algumas macros internas. Mas esta para breve.

Sem mais demoras o snippet de exemplo:

/*
* main.c
* This file is part of dbslibmenu
*
* Copyright (C) 2012 - Darcy Brás da Silva
*
* dbslibmenu is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* dbslibmenu is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dbslibmenu. If not, see <http://www.gnu.org/licenses/>.
*/

#include "libmenu_menu_interface.h"


int global=0;


void inc(void)
{
   global++;
   dys();
}

void dec(void)
{
   global--;
   dys();
}



void dys(void)
{
   system("clear");
   printf("global is now %d", global);
   getchar();
}


int main(int argc, char **argv)
{
   //criar os menus
   libmenu_menu_t menu, sub, sub2;

   //inicializa-los menu, nome menu
   libmenu_init_menu(&menu, "Main Menu");
   libmenu_init_menu(⊂, "Sub Menu");
   libmenu_init_menu(&sub2, "sub sub menu");

   //menu em causa              nome opcao   callback, NULL significa sem callback
   /* a callback necessita ser void f(void) há planos para mudar esse requisito em próximas versões */
   /* se quiserem activar uma data função sempre que saiam de um dado menu, basta inserirem uma callback
      a entrada que leva ao submenu
   */
   menu.add_entry(&menu, "Option One", NULL);
   menu.add_entry(&menu, "show global", dys);
   menu.add_entry(&menu, "increment global", inc);
   menu.add_entry(&menu, "decrement global", dec);
   menu.add_entry(&menu, "Option for next menu", NULL);

   sub.add_entry(⊂, "ana option", NULL);
   sub.add_entry(⊂, "phone option", NULL);
   sub.add_entry(⊂, "just another option", NULL);

   sub2.add_entry(&sub2, "sub sub opt", NULL);
   sub2.add_entry(&sub2, "sub sub opt2", NULL);
   sub2.add_entry(&sub2, "sub sub opt3", NULL);

   /* unir os menus */
   sub.add_submenu(⊂, &sub2);
   menu.add_submenu(&menu, ⊂);

   /* menu principal consegue representar os submenus, logo não é necessário chamar os loops dos outros */
   menu.loop(&menu);


   /* destrução para garantir que toda a memoria alocada é libertada */

   menu.self_destruct(&menu);
   sub.self_destruct(⊂); 
   sub2.self_destruct(&sub2);

   return 0;
}

Implementação que faz menor uso de malloc, para breve.

ainda não foram criadas as macros de configuração da biblioteca,

portanto, se estão no windows, abram o ficheiro

https://github.com/dardevelin/dbslibmenu/blob/master/libmenu_menu_interface.c

e redefinam LIBMENU_SYS_CLEAR_OP "clear" para LIBMENU_SYS_CLEAR_OP "cls"

PS: Não foi colocada na secção de download, visto que o repositório mantêm toda a informação no mesmo local.

Não foi adicionada a wiki, visto que ainda precisa de documentação. Assim que estiver, assim será feito se a comunidade quiser.

FeedBack Apreciado .

Link to comment
Share on other sites

Penso que podias tentar descobrir qual a forma correcta de limpar o terminal tanto em windows como linux e outros que tais e utilizar esse procedimento em vez de executares os comandos "clear" e "cls".

Algo deste género:

void clear_screen (void) {
#ifdef _WIN32
   // limpar o terminal em windows
#else
   printf("\x1b[H\x1b[J");
#endif
}

Claro que se fizeres como já disseram e implementares isso em cima do ncurses, estes problemas de portabilidade desaparecem.

Edited by pedro-kun
Link to comment
Share on other sites

Penso que podias tentar descobrir qual a forma correcta de limpar o terminal tanto em windows como linux e outros que tais e utilizar esse procedimento em vez de executares os comandos "clear" e "cls".

Algo deste género:

void clear_screen (void) {
#ifndef _WIN32
// limpar o terminal em windows
#else
printf("\x1b[H\x1b[J");
#endif
}

Claro que se fizeres como já disseram e implementares isso em cima do ncurses, estes problemas de portabilidade desaparecem.

Boas, obrigado pela dica, já tinha planos de usar a compilação condicional para cada OS, mas decidi que deveria apurar a interface da biblioteca primeiro, visto que a compilação condicional torna o código mais ilegível.

pequena nota, no exemplo que deste não seria antes



#ifndef _WIN32
  // relembro te que if n def = if not defined 
  //default
#else
 //windows
#endif

Portanto se puderem dar feedback, de como gostariam de ("criar os menus"/"forma que chamam as funções da biblioteca"), seria uma mais valida. 🙂

Alguém já experimentou ?

Alguém leu o código ? Esta legível ?

Link to comment
Share on other sites

Sim, tens razão, ali era ifdef e não ifndef (escrevo ifndef mais vezes que ifdef e saiu-me).

Quanto à legibilidade, depende... é apenas uma instrução e evita que tenhas que executar um programa externo só para limpar o ecrã. A legibilidade não é tudo, portanto 🙂

Link to comment
Share on other sites

Uns reparos a nível técnico:

no ficheiro interface tens uma função

char *_newstr(const char *str)

Eu colocaria esta função como static, embora tenhas usado o _ no nome. Não sei se alguém concorda ou discorda.

Tens uns printfs lá pelo meio que me parecem de debug. Normalmente uso o -D na makefile para passar defines de debug ao compilador, activando ou desactivando estes printfs.

Logicamente tens de mudar o tipo das callbacks... não permitir a passagem de argumentos limita demasiado a utilização da biblioteca. Talvez usar funções com argumentos variáveis, para dar o máximo de flexibilidade ao utilizador.

Utilizas globais... Eu optaria por um handler para guardar as variáveis, mas, tratando-se de uma biblioteca de IO, que à partida não será usada em concorrência, aceita-se.

EDIT: Esqueci-me de um ponto fundamental, e que toda a gente se esquece... Documentação do código... Nem que sejam uns comentarios nos headers...

Agora, a um nível mais prático. Considero essa biblioteca um excelente exercício para praticar e desenvolver conhecimentos, assim como para trabalhar a parte de organização e estruturação de código. Não leves a mal, até porque todos os contributos para a comunidade são sempre bem vindos, mas acho que a sua utilidade é muito limitada. Normalmente quem utiliza este tipo de menus são principiantes, que dificilmente têm conhecimentos para usar esse nível de C. Quem tem conhecimentos para tal, provavelmente estará mais interessado em usar a passagem de parâmetros na chamada do programa, ou ir para uma nCurses.

De toda a forma acho essencial que os Newbies passem pela fase de eles próprios implementarem os seus menus, até pelo know how que vão adquirindo, através da manipulação de strings, e IO. Usar uma biblioteca destas logo à partida vai fazer com que não aprendam muita coisa essencial (um pouco como quem começa a programar em .net).

De toda a forma, pode ser útil para quem esteja um pouco mais avançado e queira uma forma fácil e rápida de fazer uma interface, podendo dedicar o resto do tempo a trabalhar outras partes de um projecto.

Edited by Flinger
Link to comment
Share on other sites

Flinger

Boas, em primeiro lugar muito obrigado pelo feedback.

Agora respondendo à alguns pontos que fizeste:

A razão pela qual essa função não faz parte da biblioteca é porque não passa de uma implementação da strdup,

visto que a strdup embora seja implementada na maioria dos compiladores não é standard.

Quanto ao ser static, é uma boa ideia, embora o mais provável é que vá ser eliminada visto que já tinha dito que tenho intenções de reduzir drasticamente as alocações de memoria.

printfs de debug, acho que não deixei la nenhum, até porque eu uso uma macro para mensagens de debug, mas se indicares onde estão eu posso ter me enganado e tratar disso também...

As funções dys(), inc(), dec(), em conjunto com a variável global, são só uma forma para demonstrar o uso da biblioteca, o intuito não era mostrar como desenvolver uma aplicação apenas ter um teste funcional da construção dos menus.

Sim é bem provável que a biblioteca não terá nenhuma utilidade para a grande maioria das pessoas, mas eu próprio precisava de algo para protótipos rápidos de algumas ideias, por vezes é a diferença entre começar um bom projecto ou não. Quando o inicio requer algo chato é meio caminho para a preguiça aproveitar-se 🙂

A documentação não foi esquecida 😄 no primeiro post esta isto → ↓

PS: Não foi colocada na secção de download, visto que o repositório mantêm toda a informação no mesmo local.

Não foi adicionada a wiki, visto que ainda precisa de documentação. Assim que estiver, assim será feito se a comunidade quiser.

Uma das vantagens do design aplicado é que facilmente se podem adicionar opções aos menus durante o runtime da aplicação, o que significa que tornar o menu argv aware não deve ser a tarefa mais dolorosa 🙂 _quem sabe ganha entrada como novo "feature" na próxima versão_

ncurses é sem duvida interessante e visto que há interesse estou seriamente a ponderar aplicar a uma interface ncurses a coisa 🙂

A única razão faz com que as callbacks tenham o requisito de serem void(*f)(void) é para manter a consistência no type system aplicado a biblioteca.

Ou seja os callbacks seriam quase que como diferentes "main's" de uma aplicação. Onde a sua única tarefa é organizar as chamadas do mesmo e realizar uma tarefa. Afinal de contas as boas praticas dizem que uma função deve realizar uma tarefa simples e bem.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.