Llenar el espacio disponible con una columna en GridBagSizer

Estoy tratando de crear un cuadro de diálogo en el que haya algunas descripciones largas, cada una seguida de una pregunta corta con respuestas de botones de opción. Estoy poniendo mis controles con un GridBagSizer (documentos de wxWidgets, documentos de wxPython), en el que la descripción larga abarca las tres columnas, y la pregunta y las respuestas de dos botones de radio están cada una en sus propias columnas.

enter image description here

Me gustaría que las respuestas estuvieran alineadas a la derecha, así que he intentado pasar flag=wx.EXPAND al agregar el StaticText para la pregunta al diseño. Sin embargo, esto no parece tener el efecto deseado; la primera columna sigue siendo lo suficientemente grande como para que quepa la pregunta más larga, en lugar de llenar el espacio disponible y presionar los botones de opción hacia la derecha.

import wx

class MyDialog(wx.Dialog):
    def __init__(self, *args, **kwargs):
        wx.Dialog.__init__(self, *args, **kwargs)

        sizer = wx.GridBagSizer(5, 5)
        sizer.SetEmptyCellSize((0,0))
        sizer.AddGrowableCol(0)

        for i in range(5):
            description = wx.StaticText(self, -1, "This is a long description \
that may span several lines. Filler filler filler filler filler. More filler \
filler filler filler filler.")
            description.Wrap(500)

            question = wx.StaticText(self, -1, "Are you sure?")
            yes = wx.RadioButton(self, -1, "Yes", style = wx.RB_GROUP)
            no = wx.RadioButton(self, -1, "No")

            sizer.Add(description, (i*3, 0), (1, 3))
            sizer.Add(question, (i*3+1, 0), flag=wx.EXPAND)
            sizer.Add(yes, (i*3+1, 1))
            sizer.Add(no, (i*3+1, 2))

        self.SetSizerAndFit(sizer)
        self.Show()


app = wx.App(False)
dialog = MyDialog(None)
app.MainLoop()

También intenté agregar una columna espaciadora adicional y configurar wx.EXPAND en eso, pero eso tampoco ayuda. También he intentado configurar wx.ALIGN_RIGHT en los botones de radio, pero como era de esperar, eso solo hace que el botón "No" se alinee a la derecha, ya que es la tercera columna la que ocupa el resto del espacio disponible.

¿Me estoy perdiendo de algo? ¿No es posible tener una columna en un GridBagSizer expandir para llenar el espacio disponible, o lo estoy haciendo mal? Y si lo estoy haciendo mal, ¿está documentado en alguna parte? Las referencias a la documentación apropiada serían útiles.

preguntado el 22 de mayo de 12 a las 19:05

2 Respuestas

Traté de resolver su problema usando wx.Sizer.AddStretchSpacer() para llenar el vacío. http://wxpython.org/docs/api/wx.Sizer-class.html#AddStretchSpacer

Lo hice funcionar, pero los StretchSpacers parecen comportarse de manera extraña en wx.GridBagSizers

sizer.Add(description, (i*3, 0), (1, 3))
sizer.Add(question, (i*3+1, 0), flag=wx.EXPAND)
sizer.AddStretchSpacer((i*3+1,1))
sizer.Add(yes, (i*3+1, 2), flag=wx.ALIGN_RIGHT)
sizer.Add(no, (i*3+1, 3))

Tenga en cuenta cómo necesito alinear el botón Sí a la derecha. No sé por qué es esto. No importa en qué columna inserte el espaciador elástico, tuve que alinear el botón Sí a la derecha (¡incluso si el espaciador se insertó en la columna 0!). Además, el botón No aparece demasiado a la derecha. No entiendo por qué el código se comporta así y todo parece un poco raro, así que le sugiero que no use esta solución.

Sin embargo, el uso de un espaciador de estiramiento debería permitirle crear wx.BoxSizers con un ancho relativo al ancho de la ventana. Esto te permitiría alinear los botones (ya que todos tienen el mismo ancho y el espaciador elástico los empuja hacia el lado derecho). Entonces, tal como sugirió @Mike Discoll brevemente, refactoricé su código para usar wx.BoxSizers

class MyDialog(wx.Dialog):
    def __init__(self, *args, **kwargs):
        wx.Dialog.__init__(self, *args, **kwargs)

        sizer = wx.BoxSizer(wx.VERTICAL)

        for i in range(5):
            description = wx.StaticText(self, -1, "This is a long description \
that may span several lines. Filler filler filler filler filler. More filler \
filler filler filler filler.")
            description.Wrap(500)

            questionSizer = wx.BoxSizer(wx.HORIZONTAL)
            question = wx.StaticText(self, -1, "Are you sure?")
            yes = wx.RadioButton(self, -1, "Yes", style = wx.RB_GROUP)
            no = wx.RadioButton(self, -1, "No")

            sizer.Add(description)
            questionSizer.Add(question)
            questionSizer.AddStretchSpacer()
            questionSizer.Add(yes)
            questionSizer.Add(no)
            sizer.Add(questionSizer, flag=wx.EXPAND)

        self.SetSizerAndFit(sizer)
        self.Show()

Lo que creo que encontrarás te da exactamente el resultado que querías:

Estirar espaciador y wx.BoxSizer

Tenga en cuenta que debe agregar questionSizer con el indicador wx.EXPAND porque los espaciadores de estiramiento se expanden para llenar el área no utilizada. Si llama a sizer.Add() sin wx.EXPAND, entonces el ancho se establece de forma predeterminada en solo la longitud del texto y los botones (es decir, sin espacio adicional para que lo llene el espaciador de estiramiento). El uso de wx.Expand establece el ancho al ancho del medidor (que, debido a la descripción, sabemos que será de alrededor de 500 píxeles)

Puede jugar con los bordes para obtener el espacio entre los elementos de la forma que desee.

---EDITAR---

Acabo de probar un experimento en el que cambié el texto de la descripción para que sea una cadena muy corta (mucho menos de 500 píxeles) y debido a la llamada self.SetSizerAndFit(), la ventana resultante es muy, muy pequeña y visualmente desagradable:

Descripción pequeña sin tamaño mínimo

Creo que sería mejor si establece explícitamente el tamaño del medidor para garantizar un ancho mínimo constante.

[...]
width = 500 #the desired minimum width
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.SetMinSize((width,1)) #ensures sizer will be minimum width
"""NOTE: the minimum size gets recalculated when new items are added.
i.e. if height exceeds 1 pixel or if width exceeds wdith, the sizer
will recalculate the minimum size"""


for i in range(5):
    description = wx.StaticText(self, -1, "Short Description") #description is less than 500 pixels, Wrap() will have no effect.
    description.Wrap(width)
[...]

Lo que resulta en esto:

Descripción pequeña con tamaño mínimo

contestado el 23 de mayo de 12 a las 04:05

Gracias, este es el enfoque con el que finalmente opté. Quería usar GridBagSizer para mantener alineadas las respuestas de ancho variable (ya que las respuestas no siempre serán solo "sí" y "no"), pero parece que los BoxSizer apilados funcionan mejor que GridBagSizer. - Brian Campbell

Parte del problema es que estás usando SetSizerAndFit(). Intente usar solo SetSizer(). Eso ayuda mucho. Personalmente, prefiero anidar BoxSizers ya que me brinda un control más granular sobre la apariencia de mis widgets.

contestado el 22 de mayo de 12 a las 22:05

Si hago eso, mi ventana no se ajusta a los controles y no afecta si la primera columna se expande. Puedo llamar manualmente Fit(), pero eso no es sustancialmente diferente a llamar SetSizerAndFit(). estoy usando GridBagSizer para mantener alineadas las dos columnas de botones de opción; no puede hacer eso usando BoxSizers anidados (en este ejemplo, todos tienen el mismo ancho en cada fila, por lo que se alinearán, pero no funciona si tienen diferentes anchos en cada fila). - Brian Campbell

@Mike Driscoll tiene razón en que usar SetSizerAndFit() significa que debe tener mucho cuidado porque cambiará el tamaño de su cuadro de diálogo según el tamaño de su medidor. Si, por ejemplo, su dimensionador es de solo 10x10 píxeles, SetSizerAndFit() cambiará el tamaño de su diálogo en consecuencia y el resultado puede parecer bastante extraño y feo. - un pequeño

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.