Recientemente me han planteado la idea de crear una venta de Matlab para introducir matrices utilizando campos de texto. Permitiendo que la ventana soporte una matriz de tamaño arbitrario. Lo que es un reto tanto en GUIDE como App Designer. En esta entrada se va a explicar una implementación de una GUI en Matlab para cargar una matriz. Para lo que se va a codificar desde cero con los componentes de App Designer, siendo similar una implementación con GUIDE.
Nótese que para representar matrices en los GUI de Matlab existe el componente uitable()
, siendo esta la opción que se debería emplear en la mayoría de las situaciones. El objetivo de esta entrada es practicar únicamente con
Inicialización de la función
En primer lugar, se va a crear un nuevo archivo de Matlab que se llamará uiMatrix
en el que se va a escribir el código. La función que se crear tendrá dos entradas, n
y m
, que se corresponde con el número de filas y columnas respectivamente. Por otro lado, tendrá un único valor de salida que será la matriz de datos.
Así, si el usuario no indica nada, se creará una matriz cuadrada de 3 por 3. Por lo que se tendrá que sobrecargar la función. Para lo que se utilizará nargin
, cuyo valor contiene el número de parámetros introducidos. En caso de que sea cero se asigna el valor 3 a n
y m
. Si es dos, se copiará el valor de n
en m
. En caso de que sea otro numero no se hará nada. Esto es, se escribe el siguiente código.
% Default size for the matrix if nargin == 0 n = 3; m = 3; elseif nargin == 1 m = n; end
Cálculo del tamaño de la venta
Antes de crear la venta es necesario calcular el tamaño que tendrá. El cual dependerá del número de elementos. Tanto el alto como el ancho es la suma de las componentes más la separación de estas, incluyendo la separación del borde. En el caso de la altura es necesario incluir los botones de aceptar y cancelar.
Además del ancho y alto es necesario situar la ventana en una posición de la pantalla, para lo que es necesario conocer su tamaño. Afortunadamente este valore se puede obtener mediante la propiedad 'MonitorPositions'
. Una buena idea es situar esta en el centro. Así, para crear la venta se puede utilizar el código
% Get the monitor position monitor = get(0, 'MonitorPositions'); % Calculate the position of the window in the center of the monitor position_h = (n + 1) * (edit_h + sep_h) + sep_h; position_w = m * (edit_w + sep_w) + sep_w; position = [monitor(3)/2 - position_w/2, ... monitor(4)/2 - position_h/2, position_w, position_h]; % Create Figure h = uifigure('Name', sprintf('Matrix (%d, %d)', n, m), 'Position', position);
Donde edit_w
es el ancho de los campos, edit_h
es la altura, sep_w
es la separación horizontal y sep_h
es la separación vertical. Las únicas propiedades que se asigna a la ventana son el nombre con el número de elementos y la posición. Al ejecutar el código hasta aquí se obtiene.
Agregación de los campos de texto
En este punto hay que agregar los campos de texto. Para ello se creará una matriz en la que guardar los identificadores de estos para poder acceder a sus valores. Los elementos se pueden añadir mediante un par de bucles anidados.
Antes de continuar hay que tener en cuenta que el origen de coordenadas en las ventanas de Matlab es la esquina inferior izquierda. Es decir, a medida que se avanza se sube en la ventana. La situación inversa a la hora de recorrer una matriz. Por lo que, si desea que las coordenadas se mueven en el mismo sentido que una matriz se tiene que invertir el orden en el eje vertical.
Para la creación de campos de texto se usa la función uieditfield()
. A la que se le tiene que indicar el objeto padre y la posición. Opcionalmente, se puede configurar para que solamente admite valores numéricos indicando la opción numeric
. Lo que se hará para evitar tener que controlar errores. El código que se utilizará para ello es:
% Create the edit zone edit = zeros(n, m); for i = 1:n for j = 1:m edit(i, j) = uieditfield(h, 'numeric', 'Position', [... (edit_w * (j-1) + sep_w * j), ... (edit_h * (n - i + 1) + sep_h * (n - i + 2)), ... edit_w, edit_h]); end end
En este punto la ventana va tomando la forma deseada.
Venta con los campos de texto numéricosInclusión de los botones
Posteriormente hay que incluir un botón para aceptar los valores y otro para cancelar. Los que se situarán en la última fila de la ventana a la derecha. Siendo el cálculo de la posición similar a lo que se ha visto en las secciones anteriores. En ambos casos es necesario asignar una función que se ejecutará cuando se pulse el botón. Para facilitar el trabajo se usará la misma función close()
.
La función close()
tendrá que cerrar la ventana y en el caso de ser llamada desde el botón aceptar cargar los valores en los resultados. Afortunadamente el primer parámetro de la función es el objeto desde el que la llama, por lo que es fácil comprobar.
Los valores se pueden recoger mediante con un par de bucles leyendo la propiedad Value
de cada campo de texto.
% Create accept and cancel the buttons accept = uibutton(h, ..., 'ButtonPushedFcn', @close, ... 'Text', 'Accept', ... 'Position', [position_w - 2 * edit_w - 2 * sep_w, sep_h, edit_w, edit_h]); uibutton(h, ... 'ButtonPushedFcn', @close, ... 'Text', 'Cancel', ... 'Position', [position_w - edit_w - sep_w, sep_h, edit_w, edit_h]); function close(hObject, ~) % Read the values if the button is accept if (accept == hObject) values = zeros(n, m); for k = 1:n for l = 1:m values(k, l) = get(edit(k, l), 'Value'); end end end % Resume program execution and close window uiresume(h); delete(h); end
Con esto la ventana ya toma la forma final del GUI en Matlab para cargar una matriz, lo que se muestra en la siguiente captura de pantalla.
GUI final con los botones aceptar y cancelarEsperar hasta que el usuario pulse un botón
En el punto actual se puede ejecutar la aplicación, pero la aplicación no espera a que el usuario pulse un botón para devolver el resultado. Para conseguir esto es necesario indicar a Matlab que pause la ejecución de la ventana. Poner la aplicación en pausa se hace con la función uiwait()
a la que se le ha de pasar la venta. Por otro lado, para volver a poner la finalizar la ejecución de la aplicación se ha de llamar al método uiresume()
, al que también es necesario pasarle la ventana.
La llamada a uiwait()
es necesario situar al finalizar el código, ya que no se ejecutara ninguna línea situada después. Por otro lado la llama a uiwiat()
se tiene que hacer cuando se quiera salir de la aplicación. En el ejemplo será en al finalizar la función close()
.
Código completo de la aplicación
El código completo de la aplicación, incluyendo las constantes y las llamadas a las funciones uiwait()
y uiresume()
se muestra a continuación.
function values = uiMatrix(n, m) % uiMatrix - Create a figure for read matrix % % This function creates a new figure which can read arbitrary matrix % in a MATLAB Window. % Copyright 2019 Daniel Rodriguez % Default size for the matrix if nargin == 0 n = 3; m = 3; elseif nargin == 1 m = n; end % Default results values = []; % Graphic elements size constants edit_w = 100; edit_h = 22; sep_w = 6; sep_h = 6; % Get the monitor position monitor = get(0, 'MonitorPositions'); % Calculate the position of the window in the center of the monitor position_h = (n + 1) * (edit_h + sep_h) + sep_h; position_w = m * (edit_w + sep_w) + sep_w; position = [monitor(3)/2 - position_w/2, ... monitor(4)/2 - position_h/2, position_w, position_h]; % Create Figure h = uifigure('Name', sprintf('Matrix (%d, %d)', n, m), 'Position', position); % Create the edit zone edit = zeros(n, m); for i = 1:n for j = 1:m edit(i, j) = uieditfield(h, 'numeric', 'Position', [... (edit_w * (j-1) + sep_w * j), ... (edit_h * (n - i + 1) + sep_h * (n - i + 2)), ... edit_w, edit_h]); end end % Create accept and cancel the buttons accept = uibutton(h, ..., 'ButtonPushedFcn', @close, ... 'Text', 'Accept', ... 'Position', [position_w - 2 * edit_w - 2 * sep_w, sep_h, edit_w, edit_h]); uibutton(h, ... 'ButtonPushedFcn', @close, ... 'Text', 'Cancel', ... 'Position', [position_w - edit_w - sep_w, sep_h, edit_w, edit_h]); function close(hObject, ~) % Read the values if the button is accept if (accept == hObject) values = zeros(n, m); for k = 1:n for l = 1:m values(k, l) = get(edit(k, l), 'Value'); end end end % Resume program execution and close window uiresume(h); delete(h); end % Block program execution and wait to resume uiwait(h); end
Conclusiones
En esta entrada se ha visto cómo construir una GUI en Matlab para cargar una matriz de forma programática. Viéndose cómo incluir los elementos dinámicamente sin utilizar un constructor visual como GUIDE o App Designer. Aunque la funcionalidad de esta aplicación está cubierta por el componente uitable()
, su implementación sirve de ejercicio para afianzar los conceptos.
No te olvides valorar esta entrada
Suscríbete a nuestro boletín
Suscríbete al boletín semanal para estar al día de todas las publicaciones de Analytics Lane.