martes, 20 de mayo de 2014

Como colocar Widgets dentro de un QHeaderView usando el framework Qt.

clip_image002

Hace poco tuve la necesidad de lograr colocar QComboBoxes en las secciones dentro de un QHeaderView, en un inicio pensé que esto sería bastante sencillo, ya que podría utilizar el método setViewportMargins () y poner los widgets en la parte superior. Pero por desgracia esto no es posible hacerlo porque los itemviews establecen los márgenes de ventana gráfica y si se establece fuera de este puede causar problemas por lo que no se recomienda este enfoque.

Por lo tanto, la manera de conseguir que funcione es crear los widgets y colocarlos en el QHeaderView directamente, esto significa que tenemos que ajustar de forma manual cuando se cambia el tamaño de las secciones, se mueve o cuando el propio itemview se desplaza [1]. Así que para hacer esto tenemos que empezar por hacer una clase que herede de QHeaderView. En este caso me estoy centrando exclusivamente en una cabecera horizontal, ya que esto hace que sea más sencillo centrarse en lugar de tratar de darse cuenta de las secciones de dirección se están moviendo.

MyHorizontalHeader(QWidget *parent = 0) : QHeaderView(Qt::Horizontal, parent)
{
connect(this, SIGNAL(sectionResized(int, int, int)), this,
SLOT(handleSectionResized(int)));
connect(this, SIGNAL(sectionMoved(int, int, int)), this,
SLOT(handleSectionMoved(int, int, int)));
setMovable(true);
}

Hemos añadido dos slots aquí, uno es para el manejo de una sección cuando se cambia el tamaño y otro para cuando se mueve una sección. Vamos a llegar a ello dentro de poco, pero primero vamos a cubrir la inicialización de los widgets. No podemos hacer esto en el constructor (aunque podríamos hacerlo si sabemos más sobre el itemview que estará asociado, pero para este caso yo quiero ser lo más genérico posible), ya que no sabemos cuántas secciones habrá . Así que el mejor lugar para hacer esto es el método re-implementado showEvent().

void showEvent(QShowEvent *e)
{
for (int i=0;i<count();i++) {
if (!boxes[i]) {
QComboBox *box = new QComboBox(this);
boxes[i] = box;
}
boxes[i]->setGeometry(sectionViewportPosition(i), 0,
sectionSize(i) - 5, height());
boxes[i]->show();
}
QHeaderView::showEvent(e);
}

La matriz de las cajas es sólo un QMap <int, QComboBox *> con el entero que se refiere al índice lógico de la sección de la lista desplegable que está conectada. Con el fin de conseguir la posición correcta en la ventana gráfica para la sección utilizamos sectionViewportPosition () y sectionSize () se usa para obtener el tamaño de la sección. La razón del -5 existe solamente para que yo pueda ver un poco de la parte inferior para permitir el movimiento de las secciones. Si sólo desea ver el divisor para cambiar el tamaño de las secciones puede cambiar a -2.

Lo que queda entonces es reaccionar al cambio de tamaño de las secciones y el movimiento de las secciones con los siguientes slots:

void handleSectionResized(int i)
{
for (int j=visualIndex(i);j<count();j++) {
int logical = logicalIndex(j);
boxes[logical]->setGeometry(sectionViewportPosition(logical), 0,
sectionSize(logical) - 5, height());
}
}
void handleSectionMoved(int logical, int oldVisualIndex, int newVisualIndex)
{
for (int i=qMin(oldVisualIndex, newVisualIndex);i<count();i++){
int logical = logicalIndex(i);
boxes[logical]->setGeometry(sectionViewportPosition(logical), 0,
sectionSize(logical) - 5, height());
}
}

El código es fundamentalmente el mismo en ambos slots, pero el bucle está ajustado para limitar la cantidad de widgets tiene que ser tocado, cuando una sección cambia de tamaño sólo vamos a partir del índice visual de la redimensionada y llegaremos al resto de las secciones después de esa. Cuando se mueve una sección que tenemos que ir desde el primer índice visual accionado hasta el final del headerview, como las otras secciones tendrán que ser actualizados para adaptarse.
Hasta aquí todo bien, lo único que nos queda por hacer ahora es manejar el caso en que el itemview se desplaza ya que tenemos que cambiar la posición de los widgets de nuevo. Como el QHeaderView no ha sido notificado de ninguna manera que esto ocurre ya que está en la ventana que tenemos que conectar este a partir del propio itemview cuando se produce un desplazamiento. Para hacer esto necesitamos re-implementar scrollContentsBy ():

void scrollContentsBy(int dx, int dy)
{
QTableWidget::scrollContentsBy(dx, dy);
if (dx != 0)
horizHeader->fixComboPositions();
}

Lo que no se muestra aquí es el constructor que crea una instancia de la clase MyHorizontalHeader y establece con setHorizontalHeader (). Así que cada vez que el contenido se desplaza en cualquier dirección horizontal llamamos al método fixComboPositions() que hace:

void fixComboPositions()
{
for (int i=0;i<count();i++)
boxes[i]->setGeometry(sectionViewportPosition(i), 0,
sectionSize(i) - 5, height());
}

En efecto es el mismo que hace el método showEvent (), ya que no sabemos cuánto se ha hecho visible u oculta la forma más fácil es ir sobre todos los widgets.
Y ahí lo tienen, un enfoque bastante sencillo de conseguir widgets en un QHeaderView, explicado paso a paso, por lo que debería ser fácilmente adaptable para sus propias necesidades. Es todo por hoy esperamos que esta entrada le sea de utilidad.


¿Te ha gustado este Post? Compártelo con tus amigos.

No hay comentarios:

Publicar un comentario

IconIconIcon