Es muy útil cuando listamos elementos en una aplicación proporcionar al usuario una opción de búsqueda rápida. Hacemos la aplicación más amigable con poco esfuerzo extra por nuestra parte.
El ejemplo que traigo es muy parecido al ejemplo de ListStore, aunque añado la posibilidad de filtrar la lista con el texto introducido en un widget Entry.
La clave está en crear una columna más en el modelo de datos (c_visible) que determinará si el elemento es visible o no en la lista, y en lugar de hacer que nuestro TreeView siga el modelo por defecto, haremos que siga un modelo filtrado.
Por otra parte, cada vez que escribamos algo en nuestro widget Entry, recorreremos las filas de la lista para ver qué se oculta y qué se muestra.
Veamos el código:
tvsmc.h – Será el modelo de datos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /* @(#)tvsmc.h */ #ifndef _TVSMC_H #define _TVSMC_H 1 #include <gtkmm.h> using namespace Gtk; class TVSModelColumns : public TreeModel::ColumnRecord { public: TVSModelColumns(); TreeModelColumn<unsigned> c_id; TreeModelColumn<Glib::ustring> c_name; TreeModelColumn<Glib::ustring> c_description; TreeModelColumn<bool> c_visible; }; #endif /* _TVSMC_H */ |
tvsmc.cpp
1 2 3 4 5 6 7 8 9 | #include "tvsmc.h" TVSModelColumns::TVSModelColumns() { add(c_id); add(c_name); add(c_description); add(c_visible); } |
tvsexample.h – La ventana principal
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 39 40 41 42 43 44 45 46 | /* @(#)tvsexample.h */ #ifndef _TVSEXAMPLE_H #define _TVSEXAMPLE_H 1 #include <gtkmm.h> #include "tvsmc.h" using namespace Gtk; class TVSExample : public Window { public: TVSExample(); virtual ~TVSExample(); protected: /* Events */ void onButtonQuit(); bool reallyQuitQuestion(); bool onCloseButton(GdkEventAny *event); bool onKeyRelease(GdkEventKey *event); /* Other method */ void dataFill(); void insertRow(unsigned id, Glib::ustring name, Glib::ustring description); void showAllRows(); /* Data model */ TVSModelColumns columns; Glib::RefPtr<Gtk::ListStore> refTreeModel; Glib::RefPtr<Gtk::TreeModelFilter> refFilter; /* Widgets */ Box w_vBox; ScrolledWindow w_scroll; TreeView w_treeview; ButtonBox w_buttonBox; Button w_button; Box w_hBox; Label w_entryLabel; Entry w_textEntry; }; #endif /* _TVSEXAMPLE_H */ |
tvsexample.cpp
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | #include "tvsexample.h" #include <iostream> using namespace std; TVSExample::TVSExample(): w_vBox(ORIENTATION_VERTICAL), w_button("Quit"), w_hBox(ORIENTATION_HORIZONTAL), w_entryLabel("Search: ") { this->set_title("TreeView with fast search"); this->set_default_size(400,400); // Adds vertical box this->add(w_vBox); // Adds widgets to the box w_vBox.pack_start(w_hBox, PACK_SHRINK); w_vBox.pack_start(w_scroll); w_vBox.pack_start(w_buttonBox, PACK_SHRINK); // Sets widgets for search box w_hBox.pack_start(w_entryLabel); w_hBox.pack_start(w_textEntry); // Sets widgets form scrolled window w_scroll.add(w_treeview); // Configure buttonbox w_buttonBox.pack_start(w_button); w_buttonBox.set_border_width(5); w_buttonBox.set_layout(BUTTONBOX_END); // Events w_button.signal_clicked().connect(sigc::mem_fun(*this, &TVSExample::onButtonQuit)); this->signal_delete_event().connect(sigc::mem_fun(*this, &TVSExample::onCloseButton)); w_textEntry.signal_key_release_event().connect(sigc::mem_fun(*this, &TVSExample::onKeyRelease)); // Create the model refTreeModel = ListStore::create(columns); refFilter = TreeModelFilter::create(refTreeModel); refFilter->set_visible_column(columns.c_visible); w_treeview.set_model(refFilter); // Makes the treeview show some columns w_treeview.append_column("Id", columns.c_id); w_treeview.append_column("Name", columns.c_name); w_treeview.append_column("Description", columns.c_description); // Don't add c_visible // Fill treeview with data dataFill(); // Show everything this->show_all_children(); } TVSExample::~TVSExample() { } void TVSExample::onButtonQuit() { cout << "Quit button pressed"<<endl; if (reallyQuitQuestion()) this->hide(); } bool TVSExample::reallyQuitQuestion() { MessageDialog d("Are you sure you want to quit this program?", true, MESSAGE_QUESTION, BUTTONS_YES_NO, true); d.set_title("Really quit?"); return (d.run() == Gtk::RESPONSE_YES); } bool TVSExample::onCloseButton(GdkEventAny * event) { cout << "Close button pressed"<<endl; // If Yes button pressed (reallyQuitQuestion() returns true), // propagate the event. (return false) return (!reallyQuitQuestion()); } void TVSExample::dataFill() { // From: http://www.pleated-jeans.com/2012/02/20/slightly-skewed-movie-descriptions-11-pics/ // But it can be found in lots of webs insertRow(1, "Inception", "Some people fall asleep in a van"); insertRow(2, "Beauty and the Beast", "A monster locks a girl in his castle until she loves him"); insertRow(3, "The Shining", "A man goes to a hotel and writes the worst novel ever"); insertRow(4, "Frankenstein", "A lonely scientist makes a new friend"); insertRow(5, "Up", "An old man moves to a more secluded neighborhood"); insertRow(6, "Citizen Kane", "A newspaper tycoon wants to go sledding"); insertRow(7, "Lord of the Rings", "A bunch of straight men fight over jewelry"); } void TVSExample::insertRow(unsigned id, Glib :: ustring name, Glib :: ustring description) { TreeModel::Row r = *(refTreeModel->append()); r[columns.c_id] = id; r[columns.c_name] = name; r[columns.c_description] = description; r[columns.c_visible] = true; } bool TVSExample::onKeyRelease(GdkEventKey * event) { if (w_textEntry.get_text().empty()) { cout << "Empty text!" << endl; showAllRows(); } for (TreeModel::Children::iterator it = refTreeModel->children().begin(); it != refTreeModel->children().end(); ++it) { TreeModel::Row r = *it; r[columns.c_visible] = (r.get_value(columns.c_name).find(w_textEntry.get_text()) != Glib::ustring::npos); } } void TVSExample::showAllRows() { for (TreeModel::Children::iterator it = refTreeModel->children().begin(); it != refTreeModel->children().end(); ++it) { TreeModel::Row r = *it; r[columns.c_visible] = true; } } |
main.cpp – Programa principal
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include "tvsexample.h" #include <gtkmm.h> using namespace Gtk; int main(int argc, char *argv[]) { Glib::RefPtr<Application> app = Application::create(argc, argv); TVSExample window; return app->run(window); } |
Hola Gaspar,
He felicitarte por tener un blog especialmente bien cuidado, estructurado y fácil de seguir.
Tengo un problema con gtkmm sobre el que me gustaría conocer tu opinión:
Estoy desarrollando un interfaz que maneja un pequeño robot, recibe datos de sus sensores y actualiza datos procedentes de un joystick que también mueve el robotico.
Mi problema es que se pierde la conexión con la interfaz. El programa sigue funcionando en el fondo, pero la GUi se queda bloqueada.
La interfaz la he desarrollado con Glade y tengo el fichero xml conectado con un builder.
Al trabajar con varios hilos que atienden diferentes puertos y sockets, utilizo mutex para acceder al refresco de los widgets de pantalla… ¿pero… podría tratarse de demasiados accesos a la interfaz o que tengo que bloquear algo más..?… en fin… un mar de dudas…
Gracias por la atención
Un cordial saludo
Muchas gracias Lisardo.
Es difícil decirte algo sin echar un vistazo al código, pero si en el thread principal tienes el GUI y mientras haces las lecturas hay un bloqueo tal vez los mensajes no lleguen a gtk. Intenta utilizar alguna herramienta de depuración (gdb por ejemplo) o intenta crear un pequeño log (aunque sea en terminal) de lo que está sucediendo en el programa con el fin de determinar qué se efectúa para quedarse bloqueado.
Si por otro caso es lo que dices (demasiados accesos a la interfaz), lo que se puede producir si intentas acceder muchísimas veces por segundo, puedes intentar, en lugar de mandar los datos constantemente a la interfaz, escribirlos en una memoria compartida y disparar cada segundo o así una signal_timeout() que lo actualice todo.
Suerte !!
Pingback: Programadores en C++, HTML, XHTML,PHP, MySQL, CSS básico | Just another My blog Sites site /