27 Controles de selección de ficheros y directorios

Controles de selección de fichero

Ejemplo de control wxFileCtrl

Es frecuente que nuestras aplicaciones necesiten acceder o enviar datos a ficheros externos. En un capítulo más adelantado veremos los cuadros de diálogo que existen para esas tareas, las típicas de cargar, guardar o guardar como. Sin embargo, en ciertas ocasiones es preferible usar un control para seleccionar ficheros y/o directorios para las entradas o salidas para algunas tareas de nuestros programas.

Aunque los nombres de estos controles parecen indicar que sirven para la selección de ficheros o directorios, algunos controles pueden seleccionar tanto unos como otros.

Por ejemplo, wxFileCtrl permite seleccionar ambos, y lo mismo ocurre con wxGenericDirCtrl. a pesar de lo que sus nombres parecen indicar.

Control wxFileCtrl

El control de selección de ficheros más completo es wxFileCtrl. Se trata de un control mixto, que dispone de varias zonas, como puede verse a la derecha. En esta versión, por ejemplo, muestra una lista de localizaciones predeterminadas, y a la derecha la ruta actual, y un control de lista con los directorios y ficheros en la carpeta actualmente seleccionada. En la parte inferior hay un control de elección con diferentes filtros.

Aunque el nombre parezca indicar que únicamente permite seleccionar ficheros, en realidad puede seleccionar tanto ficheros como directorios.

Estilos de wxFileCtrl

Estos controles disponen de varios estilos que permiten establecer algunas opciones de funcionamiento:

  • El estilo wxFC_OPEN que es el estilo por defecto, wxFC_DEFAULT_STYLE, es el adecuado para elegir ficheros de entrada.
  • El estilo wxFC_SAVE añade un control de edición en la parte superior que permite introducir un nombre de fichero. Sigue siendo posible seleccionar un fichero existente, pero además podemos seleccionar un directorio e introducir un nombre de fichero manualmente.
  • El estilo wxFC_MULTIPLE permite seleccionar varios ficheros.

Hay que tener en cuenta que wxFC_OPEN y wxFC_MULTIPLE son incompatibles con wxFC_SAVE.

El estilo wxFC_NOSHOWHIDDEN, que únicamente está disponible en la versión genérica, oculta la casilla de verificación "Mostrar archivos ocultos" .

Creación de control wxFileCtrl

El constructor de estos controles se aparta de la regla general, y requiere los siguientes parámetros:

  • La ventana padre.
  • Un identifcador.
  • Un wxString con el directorio que se mostrará al inicio. Por defecto es wxEmptyString, que usará el directorio actual.
  • Un wxString con el nombre de fichero inicial. Por defecto es wxEmptyString, es decir, ninguno.
  • Un wxString que define el contenido del control de elección con los filtros para los nombres de fichero.
  • Los estilos.
  • La posición.
  • El tamaño.
  • Un nombre opcional.

La cadena que define los filtros tiene una sintaxis propia. Se pueden definir varios filtros, cada uno de los cuales se mostrará como una opción en el control de elección de filtro.

Cada filtro se compone de dos partes, una etiqueta y el filtro propiamente dicho, separados con un carácter '|'. Por ejemplo "Archivos header (*.h)|*.h".

Se pueden especificar varios filtros, separados con el mismo carácter '|'. Por ejemplo "Archivos header (*.h)|*.h|Archivos fuente (*.cpp)|*.cpp".

Si no se indica ningún filtro se añade uno que incluya todos los ficheros, "*":

    fileCtrl = new wxFileCtrl(this, idFileCtrl, wxEmptyString, wxEmptyString, 
        _T("Archivos header (*.h)|*.h|Archivos fuente (*.cpp)|*.cpp"), wxFC_SAVE);

Obtener selección desde wxFileCtrl

Para obtener el fichero o ficheros seleccionados disponemos de varios métodos.

El método GetDirectory obtiene el directorio actualmente seleccionado.

En el caso de controles de selección única usaremos el método GetFilename para obtener el nombre del fichero o directorio seleccionado. Para controles de selección múltiple se usa GetFilenames.

Si preferimos obtener la ruta completa, podemos usar GetPath para controles de selección única y GetPaths para los de selección múltiple.

    path = fileCtrl->GetDirectory();
    file = fileCtrl->GetFilename();

Establecer valores en wxFileCtrl

Estos controles también disponen de métodos para modificar los valores mostrados y las selecciones. Por ejemplo, SetDirectory permite elegir el directorio actual, SetFilename permite seleccionar un fichero, o SetPath realiza las dos acciones a la vez.

Eventos wxFileCtrl

Estos controles pueden emitir varios tipos de eventos, pasando como parámetro un objeto wxFileCtrlEvent.

Cada vez que se seleccione o deseleccione un archivo se envía un evento EVT_FILECTRL_SELECTIONCHANGED.

Cada vez que se cambia de directorio se envía un evento EVT_FILECTRL_FOLDERCHANGED.

Cada vez que se cambia el filtro se envía un evento EVT_FILECTRL_FILTERCHANGED.

Si el usuario hace doble clic o pulsa intro se activará la selección y se enviará un evento EVT_FILECTRL_FILEACTIVATED.

Control wxFilePickerCtrl

Ejemplo de control wxFilePickerCtrl

El control wxFilePickerCtrl tiene la misma función, pero ocupando mucho menos espacio.

Cuando se usa para cargar el control muestra un botón con el nombre del fichero seleccionado. Al pulsar sobre él se despliega un diálogo de selección de fichero que funciona del mismo modo que el control wxFileCtrl.

Cuando se usa para salvar sólo se muestra un botón con un texto para seleccionar un fichero o tres puntos en su versión reducida.

Estilos de wxFilePickerCtrl

Este tipo de controles permiten más personalización. Para ello dispone de varios estilos:

  • wxFLP_OPEN: crea un control para abrir un fichero existente.
  • wxFLP_SAVE: crea un control que permite elegir un fichero para sobrescribirlo o un nuevo fichero en la carpeta seleccionada.

Junto con wxFLP_OPEN se pueden añadir otros estilos, wxFLP_FILE_MUST_EXIST sólo permitirá elegir un fichero existente, aunque aún se puede elegir un fichero inexistente en ciertas circunstancias.

Junto con wxFLP_SAVE se puede añadir el estilo wxFLP_OVERWRITE_PROMPT, que mostrará un cuadro de diálogo si se elige un archivo existe, de modo que se proporciona al usuario una oportunidad de usar otro nombre de fichero.

El estilo wxFLP_USE_TEXTCTRL añade un cuadro de edición gestionado por el propio control, en el que se mostrará la ruta del fichero elegido y también permite al usuario escribir o modificar el nombre y ruta del fichero.

Este estilo es más útil cuando se usa con wxFLP_SAVE, pero también se puede usar con wxFLP_OPEN.

El estilo wxFLP_CHANGE_DIR recordará el directorio seleccionado por el usuario entre diferentes llamadas al diálogo que contiene el control, en lugar de usar siempre el valor por defecto.

Finalmente, el estilo wxFLP_SMALL afecta al aspecto del control, cambiando el texto del botón que despliega el diálogo de selección de fichero por "...".

El estilo por defecto, wxFLP_DEFAULT_STYLE equivale a wxFLP_OPEN | wxFLP_FILE_MUST_EXIST, y añade wxFLP_USE_TEXTCTRL en Windows y OSx.

A diferencia de los controles wxFileCtrl, estos controles no permiten selección múltiple.

Creación de control wxFilePickerCtrl

El constructor de estos controles requiere los siguientes parámetros:

  • La ventana padre.
  • Un identifcador.
  • Un wxString con la ruta iniciar del fichero. Por defecto es wxEmptyString.
  • Un wxString con el texto que se mostrará en la barra de título del cuadro de diálogo cuando se despliegue.
  • Un wxString que define el contenido del control de elección con los filtros para los nombres de fichero.
  • La posición.
  • El tamaño.
  • Los estilos.
  • Un nombre opcional.

La cadena con los filtros sigue las mismas reglas que para los controles wxFileCtrl.

Obtener selección desde wxFilePickerCtrl

Para obtener el resultado de la selección disponemos del método GetFileName, que devuelve un objeto wxFileName, y no una cadena.

La clase wxFileName dispone de varios métodos útiles para trabajar con nombres de fichero, como averiguar si la ruta existe, obtener partes del nombre, como la ruta, el nombre o la extensión, fechas de acceso y creación, etc.

Por ejemplo, para recuperar el nombre del fichero, sin la ruta completa, se puede usar el método GetFullName.

Si queremos recuperar la ruta completa podemos usar directamente el método GetPath de wxFilePickerCtrl.

    wxFileName fileName;
    fileName = filePickerCtrlSave->GetFileName();
    name1 = fileName.GetFullName();
    path2 = filePickerCtrlLoad->GetPath();

Eventos de wxFilePickerCtrl

Estos controles envía un evento EVT_FILEPICKER_CHANGED cada vez que cambia la selección, usando como parámetro un objeto de la clase wxFileDirPickerEvent.

Control wxGenericDirCtrl

Ejemplo de wxGenericDirCtrl

Los controles wxGenericDirCtrl se pueden usar para capturar tanto nombres de fichero como directorios. Muestran un control de árbol con la estructura de directorios por los que el usuario puede navegar.

Estilos de wxGenericDirCtrl

Por defecto, estos controles muestran tanto directorios como ficheros, pero eligiendo los estilos podemos afinar su comportamiento.

  • El estilo wxDIRCTRL_DIR_ONLY mostrará únicamente directorios.
  • El estilo wxDIRCTRL_SELECT_FIRST seleccionará el primer archivo o directorio de la ruta predeterminada.
  • El estilo wxDIRCTRL_MULTIPLE permite seleccionar varios archivos o directorios.
  • El estilo wxDIRCTRL_EDIT_LABELS permite renombrar archivos y directorios en su lugar.

Si no se especifica wxDIRCTRL_DIR_ONLY también se mostrarán archivos, y podrán seleccionarse tanto archivos como directorios.

En este caso se podrán utilizar filtros, de forma análoga a los de los controles anteriores.

Si además se activa el estilo wxDIRCTRL_SHOW_FILTERS se podrá seleccionar el filtro deseado, si se han definido varios. Los filtros sólo se aplican a nos nombres de archivo y no a los directorios.

Creación de control wxGenericDirCtrl

El constructor de estos controles requiere los siguientes parámetros:

  • La ventana padre.
  • Un identifcador.
  • Un wxString con la ruta iniciar. Por defecto es wxDirDialogDefaultFolderStr, que es el directorio raíz.
  • La posición.
  • El tamaño.
  • Los estilos.
  • Un wxString con los filtros.
  • Un valor entero con el índice, empezando en cero, del filtro a seleccionar.
  • Un nombre opcional.

La cadena con los filtros sigue las mismas reglas que para los controles wxFileCtrl.

    genericDirCtrl = new wxGenericDirCtrl(this, idGenericDir, wxDirDialogDefaultFolderStr, wxDefaultPosition, wxSize(400,400),
        wxDIRCTRL_DEFAULT_STYLE|wxDIRCTRL_SELECT_FIRST|wxDIRCTRL_EDIT_LABELS|wxDIRCTRL_SHOW_FILTERS,
        _T("Archivos header (*.h)|*.h|Archivos fuente (*.cpp)|*.cpp"), 1);

Recuperar selección desde wxGenericDirCtrl

En caso de que no se haya activado el estilo de selección múltiple podemos recuperar la ruta del fichero seleccionado mediante el método GetFilePath. En el caso de un control de selección múltiple deberemos usar GetFilePaths.

También podemos usar el método GetPath, que sirve tanto para recuperar directorios como ficheros. En caso de controles de selección múltiple usaremos GetPaths.

    path = genericDirCtrl->GetPath();
    file = genericDirCtrl->GetFilePath();

Ruta inicial en wxGenericDirCtrl

Para modificar la ruta inicial del control podemos establecerla al crear el control, mediante el tercer parámetro, o posteriormente, mediante el método SetPath.

Es probable que también haya que expandir la ruta para que se muestre correctamente el contenido, usando ExpandPath.

    genericDirCtrl->SetPath("/home/salvador/Programas/wx/wx027/");
    genericDirCtrl->ExpandPath("/home/salvador/Programas/wx/wx027/");

Seleccionar elementos en wxGenericDirCtrl

Es posible que queramos seleccionar uno o varios directorios o ficheros. Usaremos SelectPath para seleccionar o deseleccionar un fichero o directorio, o SelectPaths para seleccionar varios.

El método UnselectAll elimina todas las selecciones.

Eventos wxGenericDirCtrl

Estos controles emiten un evento EVT_DIRCTRL_SELECTIONCHANGED cuando se cambia el directorio seleccionado.

También emiten un evento EVT_DIRCTRL_FILEACTIVATED cuando el usuario selecciona un fichero usando doble clic o enter.

Estos eventos envías un objeto wxTreeEvent.

Control wxDirPickerCtrl

Ejemplo de wxDirPickerCtrl

Los controles "picker" son botones que al ser pulsados abren un diálogo que permite hacer una selección sencilla.

En el caso de wxDirPickerCtrl, permiten seleccionar un directorio.

Estilos de wxDirPickerCtrl

Disponemos de algunos estilos que pueden modificar el comportamiento del control.

  • El estilo wxDIRP_USE_TEXTCTRL añade un control de edición a la izquierda del botón que muestra la selección actual y permite que el usuario introduzca o modifique la ruta del directorio seleccionado.
  • El estillo wxDIRP_DIR_MUST_EXIST sólo permite seleccionar directorios existentes. Sin embargo, este estilo se ignora si se especifica wxDIRP_USE_TEXTCTRL.
  • El estilo wxDIRP_CHANGE_DIR cambia el directorio de trabajo actual cada vez que el usuario cambia la selección del directorio.

El estilo por defecto, wxDIRP_DEFAULT_STYLE, que equivale a wxDIRP_DIR_MUST_EXIST y, en wxMSW también wxDIRP_USE_TEXTCTRL. Hay que tener cuidado al usar el estilo por defecto en GTK, ya que al intentar navegar a través de los diferentes directorios se produce un error de ejecución. (al menos eso me pasa en mi equipo, probablemente se trata de un bug, pero no he podido verificarlo.)

El esitlo wxDIRP_SMALL sustituye el botón por una versión más pequeña con el texto "...".

Si se especifica el estilo wxDIRP_DIR_MUST_EXIST el botón muestra la selección actual.

Más abajo veremos que procesando el evento EVT_DIRPICKER_CHANGED podemos evitar que el programa falle al usar el estilo wxDIRP_DIR_MUST_EXIST.

Creación de control wxDirPickerCtrl

El constructor de estos controles requiere los siguientes parámetros:

  • La ventana padre.
  • Un identifcador.
  • Un wxString con la ruta iniciar. Por defecto es wxDirDialogDefaultFolderStr, que es el directorio raíz.
  • Un wxString con el texto del título del cuadro de diálogo que se muestra al navegar.
  • La posición.
  • El tamaño.
  • Los estilos.
  • Un validador.
  • Un nombre opcional.

Usar validadores

Estos controles permiten usar un validador, que podemos aprovechar para limitar los rutas que el usuario pueda seleccionar o crear.

Ya vimos en capítulos anteriores cómo crear validadores, pero veamos como crear uno para evitar que el usuario elija un directorio en el que no se pueda escribir.

Empezaremos por declarar una clase derivada de wxValidator:

#include <wx/validate.h>
...
class dirValidator : public wxValidator {
public:
    dirValidator(wxString *p=nullptr);
    virtual wxObject* Clone() const { return new dirValidator(*this); }
    virtual bool TransferFromWindow();
    virtual bool TransferToWindow();
    virtual bool Validate(wxWindow * parent);
private:
    wxString *m_path;
    DECLARE_DYNAMIC_CLASS( dirValidator )
    DECLARE_EVENT_TABLE()
};

Tendremos que definir el constructor y los métodos TransferFromWindow, TransferToWindow y Validate. Los dos primeros son triviales, se limitan a verificar que el control al que están asociados es realmente un wxDirPickerCtrl, y a leer o escribir la selección actual.

El método Validate es el que debe verificar si en la ruta elegida se pueden modificar o añadir ficheros.

bool dirValidator::Validate(wxWindow* parent)
{
    if (!m_validatorWindow->IsEnabled()) return true;

    bool retval;

    try {
        wxDirPickerCtrl *dpc = dynamic_cast<wxDirPickerCtrl*>(m_validatorWindow);
        *m_path = dpc->GetPath();
    }
    catch (...) {
        wxFAIL_MSG( _T("dirValidator sólo funciona con wxDirPickerCtrl"));
        return false;
    }

    wxFileName dir(*m_path);
    retval = dir.IsDirWritable(); // Verificar ruta
    if(!retval) {
        wxString errmsg = wxT("El directorio debe tener permiso de escritura.");
        wxMessageDialog dlg(parent,errmsg,wxT("Entrada incorrecta"),wxOK);
        dlg.ShowModal();
    }

    return retval;
}

La parte en negrita crea un objeto wxFileName a partir de la ruta elegida. Esto nos permite acceder a su método IsDirWritable, que devuelve true si es posible editar o añadir ficheros a esa ruta.

También tendremos que usar el parámetro en el constructor indicando el validador:

    dirPickerCtrl = new wxDirPickerCtrl(this, idDirPicker, wxEmptyString, _T("Seleccionar directorio"),
        wxDefaultPosition, wxSize(400,wxDefaultCoord), wxDIRP_USE_TEXTCTRL, dirValidator(&path));

Y por último, validar y transferir el contenido del control.

void DirPickerCtrl::OnOk(wxCommandEvent& event) {
    if ( Validate() && TransferDataFromWindow() ) EndModal(wxID_OK);
}

Recuperar selección desde wxDirPickerCtrl

Para recuperar el valor de la selección actual disponemos de dos métodos.

GetDirName recupera la selección en un objeto wxFileName, mientras que GetPath lo recupera en una cadena de texto.

Establecer el valor de wxDirPickerCtrl

De forma simétrica podemos establecer la selección del control desde el programa usando los métodos SetDirName, usando un objeto wxFileName o mediante SetPath, usando una cadena.

Establecer el directorio inicial en wxDirPickerCtrl

El método SetInitialDirectory sirve para establecer el directorio que se mostrará inicialmente. En ciertas combinaciones con el valor del parámetro path del constructor o con el estilo wxDIRP_CHANGE_DIR puede ser ignorado.

Eventos wxDirPickerCtrl

Este control únicamente emite un evento EVT_DIRPICKER_CHANGED cada vez que el usuario cambia la selección.

Podemos usar este evento para evitar que el programa falle cuando se usa el estilo wxDIRP_DIR_MUST_EXIST y se intenta navegar por diferentes rutas.

De todos modos, parece que usar estos controles con el estilo wxDIRP_DIR_MUST_EXIST es bastante inestable, así que no aconsejo usar esta combinación.

wxBEGIN_EVENT_TABLE(DirPickerCtrl, wxDialog)
...
    EVT_DIRPICKER_CHANGED(idDirPicker, DirPickerCtrl::SelChanged)
wxEND_EVENT_TABLE()
...
void DirPickerCtrl::SelChanged(wxFileDirPickerEvent& event) {
    dirPickerCtrl->SetPath(event.GetPath());
    dirPickerCtrl->SetInitialDirectory(event.GetPath());
}

Este evento envía un objeto de la clase wxFileDirPickerEvent, del que sólo nos interesa su método GetPath.

Ejemplo 27

Nota: a partir de este capítulo no se incluirán distintas versiones de los ejemplos para Windows y Linux. En su lugar se usará un único fichero comprimido con los ficheros fuentes que podrán ser compilados en ambos sistemas.
Para compilar los ejemplos será necesario crear un nuevo proyecto en el IDE que se prefiera usar y añadir los ficheros fuente suministrados.
Esto evitará problemas derivados del uso de diferentes versiones de las librerías que cada uno pueda tener instaladas.

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 27 wx027.zip 2025-11-20 10352 bytes 6