Controles con posición y tamaño adaptativo
IronPython - Diseño WinForms: Manejar el evento Resize
En la sección anterior hemos visto como posicionar y dar tamaño a los controles. Habíamos creado un script que muestra un formulario con un botón centrado:
# -*- coding: utf-8 -*-
import sys, clr
clr.AddReference('System.Windows.Forms')
clr.AddReference('System.Drawing')
from System.Windows.Forms import *
from System.Drawing import *
class MainForm(Form):
def __init__(self):
Form.__init__(self)
self.Text = 'Mi App'
self.Icon = Icon.ExtractAssociatedIcon(sys.executable)
self.ClientSize = Size(300, 100)
self.MaximizeBox = False
self.FormBorderStyle = FormBorderStyle.FixedSingle
self.add_controls()
def add_controls(self):
button = Button()
button.Text = 'Un botón'
button.Size = Size(100, 50)
x = int((self.ClientSize.Width - button.Size.Width) / 2)
y = int((self.ClientSize.Height - button.Size.Height) / 2)
button.Location = Point(x, y)
self.Controls.Add(button)
if __name__ == '__main__':
Application.EnableVisualStyles()
Application.Run(MainForm())
El resultado fue el siguiente:
El problema era que al cambiar el tamaño del formulario el botón ya no estaría centrado, por lo que para impedir que el usuario pueda cambiar el tamaño del formulario añadimos lo siguiente en el método __init__
:
self.MaximizeBox = False
self.FormBorderStyle = FormBorderStyle.FixedSingle
Pero si queremos que la aplicación muestre una ventana de tamaño variable y que el botón permanezca siempre centrado hay que buscar otra solución.
Evento Resize
del formulario
Los objetos de la clase System.Windows.Forms.Form
tienen definido un evento Resize
que nos permite ejecutar una función (manejador de eventos) cuando se produce dicho evento.
El evento Resize
se dispara cuando cambian las dimensiones del formulario, bien porque el usuario hace click en una esquina y arrastra para aumentar y disminuir su tamaño, o bien porque se maximiza o minimiza el formulario haciendo click en los correspondientes botones de la caja de control.
En primer lugar vamos a hacer que nuestro formulario pueda redimensionarse:
def __init__(self):
Form.__init__(self)
self.Text = 'Mi App'
self.Icon = Icon.ExtractAssociatedIcon(sys.executable)
self.ClientSize = Size(300, 100)
#self.MaximizeBox = False
#self.FormBorderStyle = FormBorderStyle.FixedSingle
self.add_controls()
A continuación vamos a eliminar el Location
del botón dentro del método add_controls
, y además vamos a hacer que el botón sea un atributo de MainForm
(cambiando button
por self.button
):
def add_controls(self):
self.button = Button()
self.button.Text = 'Un botón'
self.button.Size = Size(100, 50)
#x = (self.ClientSize.Width - button.Size.Width) / 2
#y = (self.ClientSize.Height - button.Size.Height) / 2
#button.Location = Point(x, y)
self.Controls.Add(self.button)
Hemos cambiado button
por self.button
porque ahora vamos a crear un método llamado relocate_controls
en el que tenemos que utilizar el botón. En este método es donde vamos a asignar el Location
del botón:
def relocate_controls(self, *args):
x = int((self.ClientSize.Width - self.button.Size.Width) / 2)
y = int((self.ClientSize.Height - self.button.Size.Height) / 2)
self.button.Location = Point(x, y)
Fíjare en que este método recibe un número arbitrario de argumentos (*args
). Esto es así porque a continuación utilizaremos este método como un manejador de eventos.
Ahora, en el método __init__
invoca al método que acabamos de crear y registralo como manejador del evento Resize
:
def __init__(self):
Form.__init__(self)
self.Text = 'Mi App'
self.Icon = Icon.ExtractAssociatedIcon(sys.executable)
self.ClientSize = Size(300, 100)
self.add_controls()
self.relocate_controls() # Invocamos al método para que se centre al inicio
self.Resize += self.relocate_controls # Registramos el método como manejador del evento Resize
Al registrar el método relocate_controls
como manejador del evento Resize
, la posición del botón se ajustará siempre que cambien las dimensiones del formulario, permaneciendo el botón centrado en todo momento:
Cuando el tamaño del formulario cambia se modifica el valor de self.ClientSize
y se dispara el evento Resize
, de manera que se ejecuta el método relocate_controls
, en el que se calcula el valor de las variables x
e y
en función del valor actual de self.ClientSize
. Finalmente, se asigna un nuevo valor para la propiedad Location
del botón: self.button.Location = Point(x, y)
.
Código final
Ya sabes como adaptar dinámicamente la posición de los controles en función del tamaño actual del formulario manejando el evento Resize
. El código final de esta sección es el siguiente:
# -*- coding: utf-8 -*-
import sys, clr
clr.AddReference('System.Windows.Forms')
clr.AddReference('System.Drawing')
from System.Windows.Forms import *
from System.Drawing import *
class MainForm(Form):
def __init__(self):
Form.__init__(self)
self.Text = 'Mi App'
self.Icon = Icon.ExtractAssociatedIcon(sys.executable)
self.ClientSize = Size(300, 100)
self.add_controls()
self.relocate_controls()
self.Resize += self.relocate_controls
def add_controls(self):
self.button = Button()
self.button.Text = 'Un botón'
self.button.Size = Size(100, 50)
self.Controls.Add(self.button)
def relocate_controls(self, *args):
x = int((self.ClientSize.Width - self.button.Size.Width) / 2)
y = int((self.ClientSize.Height - self.button.Size.Height) / 2)
self.button.Location = Point(x, y)
if __name__ == '__main__':
Application.EnableVisualStyles()
Application.Run(MainForm())
Como ejercicio intenta que el tamaño del botón también se ajuste proporcionalmente al tamaño del formulario, ¿cómo lo harías? tendrías que reasignar un nuevo valor a la propiedad Size
del botón siempre que las dimensiones del formulario cambien.
En la siguiente sección veremos un ejemplo con 6 botones con tamaño y posición adaptativos.
Un saludo!