Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Qt. Delegados. Usar diferente widget para editar y para mostrar

Estas en el tema de Qt. Delegados. Usar diferente widget para editar y para mostrar en el foro de C/C++ en Foros del Web. Hola: Sigo con el tema del Modelo-Vista-Delegados Estaba haciendo delegados personalizados para las columnas de mi ejemplo, cuando he querido hacer un delegado en el ...
  #1 (permalink)  
Antiguo 13/03/2015, 18:14
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Qt. Delegados. Usar diferente widget para editar y para mostrar

Hola:

Sigo con el tema del Modelo-Vista-Delegados

Estaba haciendo delegados personalizados para las columnas de mi ejemplo, cuando he querido hacer un delegado en el cual el editor para la entrada de datos me gustaría que fuera diferente al editor que los muestre.

Mi idea es la siguiente:

El editor de entrada de datos en un QComboBox, y maneja 3 indices, cada uno con un icono y una leyenda.

El editor que muestra los datos es un QLabel, y ha de mostrar el icono que le corresponda.

Este es el código de esa parte:
Código C++:
Ver original
  1. QWidget* MiDelegado3::createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  2. {
  3.     Q_UNUSED (index);
  4.     Q_UNUSED (option);
  5.     QComboBox* miCombo = new QComboBox(parent);
  6.     QIcon icono1, icono2, icono3;
  7.     icono1.addFile(QStringLiteral(":/iconos/alegre.png"), QSize(), QIcon::Normal, QIcon::Off);
  8.     miCombo->addItem(icono1, "Alegre");
  9.     icono2.addFile(QStringLiteral(":/iconos/serio.png"), QSize(), QIcon::Normal, QIcon::Off);
  10.     miCombo->addItem(icono2, "Serio");
  11.     icono3.addFile(QStringLiteral(":/iconos/triste.png"), QSize(), QIcon::Normal, QIcon::Off);
  12.     miCombo->addItem(icono3, "Triste");
  13.     return miCombo;
  14. }

Y ahora la parte que me da problemas:

Modo "clásico"....esta forma me cierra el programa de forma abrupta

Código C++:
Ver original
  1. void MiDelegado3::setEditorData (QWidget * editor, const QModelIndex & index) const
  2. {
  3.     QLabel* miEditor = static_cast<QLabel*>(editor);
  4.     switch (index.data().toInt())
  5.     {
  6.     case 0:
  7.     {
  8.         miEditor->setPixmap(QPixmap(QString::fromUtf8(":/iconos/alegre.png")));
  9.         break;
  10.     }
  11.     case 1:
  12.     {
  13.         miEditor->setPixmap(QPixmap(QString::fromUtf8(":/iconos/serio.png")));
  14.         break;
  15.     }
  16.     case 2:
  17.     {
  18.         miEditor->setPixmap(QPixmap(QString::fromUtf8(":/iconos/triste.png")));
  19.         break;
  20.     }
  21.     default:
  22.         break;
  23.     }
  24. }

Mi intento:
Código C++:
Ver original
  1. void MiDelegado3::setEditorData (QWidget * editor, const QModelIndex & index) const
  2. {
  3.     QLabel* miEditor = static_cast<QLabel*>(editor);
  4.     switch (index.data().toInt())
  5.     {
  6.     case 0:
  7.     {
  8.         delete miEditor;
  9.         QLabel* miEditor = new QLabel(editor->parentWidget());
  10.         miEditor->setPixmap(QPixmap(QString::fromUtf8(":/iconos/alegre.png")));
  11.         break;
  12.     }
  13.     case 1:
  14.     {
  15.        .............................
  16. //etc
  17.     }
  18. }

El problema de esta forma es que no estoy ligando el editor al widget padre, por lo que aparece flotando en la pantalla y obviamente permanece ahí después de haber cerrado la tabla.
Pensaba que de esta forma podría ligar el editor al padre del editor que se pasa como parámetro, pero no funciona
QLabel* miEditor = new QLabel(editor->parentWidget());

En fin, como siempre, muchas gracias
__________________
Mi calculadora en Qt
  #2 (permalink)  
Antiguo 14/03/2015, 02:05
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

En "createEditor" has creado un QComboBox, luego en "setEditorData" y "setModelData" has de interactuar con un QComboBox, no con un QLabel.

El widget o el mecanismo que use Qt para visualizar los datos fuera de la edición no es algo que te importe ahora mismo. Tú estás jugando ahora mismo con la edición.

* "createEditor" es una función que te permite personalizar el widget de edición que se va a usar al editar el contenido de una celda determinada

* "setEditorData" te permite definir el contenido para el widget creado anteriormente. Dado que has creado un widget que no es el que viene por defecto, tu eres el responsable de rellenarlo.

* "setModelData" te permite recuperar el contenido del widget de edición para almacenarlo en el modelo. Lo mismo que antes, es tu widget y tu tienes que saber cómo recuperar el contenido.

setEditorData debería quedar tal que (presupongo que en el modelo estás almacenando un int a modo de índice del icono. Si almacenas la ruta del icono tendrás que modificarlo):

Código C++:
Ver original
  1. void MiDelegado3::setEditorData (QWidget * editor, const QModelIndex & index) const
  2. {
  3.     QComboBox* miEditor = static_cast<QComboBox*>(editor);
  4.     miEditor->setCurrentIndex( index.data( ).toInt( ) );
  5. }

y, para recuperar el contenido:

Código C++:
Ver original
  1. void MiDelegado3::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
  2. {
  3.     QComboBox* miEditor = static_cast<QComboBox*>(editor);
  4.     model->setData( index, miEditor->currentIndex( ) );
  5. }

No puedo compilar el código porque no tengo el modelo completo. No obstante creo que este código debería funcionar.

Un saludo.
  #3 (permalink)  
Antiguo 15/03/2015, 10:25
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Hola eferion:

Ahora pondré el código completo. Realmente no es más que una excusa para poner en práctica todas las cosas que he aprendido.

Así que me he creado una estructura tal que así:
Código C++:
Ver original
  1. enum eEstado{feliz,normal,triste};
  2.  
  3. struct Persona
  4. {
  5.     QString Nombre;
  6.     QString Sexo;
  7.     int Edad;
  8.     eEstado estado;
  9. };

Y este es el modelo:

mimodelo.h
Código C++:
Ver original
  1. #ifndef MIMODELO_H
  2. #define MIMODELO_H
  3.  
  4. #include <QAbstractTableModel>
  5. #include <QDebug>
  6.  
  7. enum eEstado{feliz,normal,triste};
  8.  
  9. struct Persona
  10. {
  11.     QString Nombre;
  12.     QString Sexo;
  13.     int Edad;
  14.     eEstado estado;
  15. };
  16.  
  17. const int FILAS=4;
  18. const int COLUMNAS=4;
  19.  
  20. class MiModelo : public QAbstractTableModel
  21. {
  22.     Q_OBJECT
  23. public:
  24.     explicit MiModelo(QObject *parent = 0);
  25.     ~MiModelo();
  26.     int rowCount (const QModelIndex & parent = QModelIndex()) const;
  27.     int columnCount (const QModelIndex & parent = QModelIndex()) const;
  28.     QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole);
  29.     QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
  30.     Qt::ItemFlags flags(const QModelIndex &index) const;
  31.     bool setData(const QModelIndex & index, const QVariant& value, int role);
  32.  
  33. private:
  34.  
  35.     Persona persona[FILAS];
  36.     QStringList cabecera;
  37. };
  38.  
  39. #endif // MIMODELO_H

mimodelo.cpp

Código C++:
Ver original
  1. #include "mimodelo.h"
  2.  
  3. MiModelo::MiModelo(QObject *parent) : QAbstractTableModel(parent)
  4. {
  5.     cabecera<<"Nombre"<<"Sexo"<<"Edad"<<"Animo";
  6.     for (int i=0;i<FILAS;i++)
  7.     {
  8.         persona[i].Nombre="Noname";
  9.         persona[i].Sexo="Undefined";
  10.         persona[i].Edad=0;
  11.         persona[i].estado=normal;
  12.     }
  13. }
  14.  
  15. MiModelo::~MiModelo()
  16. {
  17.     for (int i=0;i<FILAS;i++)
  18.     {
  19.         qDebug()<<persona[i].Nombre<<" - "<<persona[i].Sexo<<" - "<<persona[i].Edad;
  20.     }
  21. }
  22.  
  23. int MiModelo::rowCount (const QModelIndex & parent) const
  24. {
  25.     Q_UNUSED (parent);
  26.     return FILAS;
  27. }
  28.  
  29. int MiModelo::columnCount (const QModelIndex & parent) const
  30. {
  31.     Q_UNUSED (parent);
  32.     return COLUMNAS;
  33. }
  34.  
  35. QVariant MiModelo::headerData (int section, Qt::Orientation orientation, int role)
  36. {
  37.     qDebug()<<"Funcion headerdata";
  38.     /*{
  39.         if (orientation == Qt::Horizontal)
  40.         {
  41.             if (role == Qt::DisplayRole)
  42.             {
  43.                 qDebug()<<cabecera.at(section);
  44.                 return cabecera.at(section);
  45.             }
  46.         }
  47.         qDebug()<<cabecera.at(section);
  48.         return QAbstractTableModel::headerData(section, orientation, role);
  49.     }*/
  50. }
  51.  
  52. QVariant MiModelo::data (const QModelIndex & index, int role) const
  53. {
  54.     if (!index.isValid())
  55.     {
  56.         return QVariant();
  57.     }
  58.     if (role == Qt::DisplayRole || role == Qt::EditRole)
  59.     {
  60.         if (index.column()==0)
  61.         {
  62.             return persona[index.row()].Nombre;
  63.         }
  64.         if (index.column()==1)
  65.         {
  66.             return persona[index.row()].Sexo;
  67.         }
  68.         if (index.column()==2)
  69.         {
  70.             return persona[index.row()].Edad;
  71.         }
  72.         if (index.column()==3)
  73.         {
  74.             return persona[index.row()].estado;
  75.         }
  76.     }
  77.     return QVariant();
  78. }
  79.  
  80. Qt::ItemFlags MiModelo::flags(const QModelIndex &index) const
  81. {
  82.     if (!index.isValid())
  83.         return 0;
  84.     return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
  85. }
  86.  
  87. bool MiModelo::setData(const QModelIndex & index, const QVariant& value, int role)
  88. {
  89.     if (index.isValid() && (role == Qt::EditRole || role == Qt::DisplayRole))
  90.     {
  91.         if (index.column()==0)
  92.         {
  93.             persona[index.row()].Nombre=value.toString();
  94.         }
  95.         if (index.column()==1)
  96.         {
  97.             persona[index.row()].Sexo=value.toString();
  98.         }
  99.         if (index.column()==2)
  100.         {
  101.             persona[index.row()].Edad=value.toInt();
  102.         }
  103.         if (index.column()==3)
  104.         {
  105.             switch (value.toInt())
  106.             {
  107.             case 0:
  108.             {
  109.                 persona[index.row()].estado=eEstado::feliz;
  110.                 break;
  111.             }
  112.             case 1:
  113.             {
  114.                 persona[index.row()].estado=eEstado::normal;
  115.                 break;
  116.             }
  117.             case 2:
  118.             {
  119.                 persona[index.row()].estado=eEstado::triste;
  120.                 break;
  121.             }
  122.             default:
  123.                 break;
  124.             }
  125.         }
  126.         emit dataChanged(index, index);
  127.         return true;
  128.     }
  129.     return false;
  130. }

Y luego un delegado por cada columna.
He usado un delegado para la columna del nombre, en este caso con el único objetivo de usar expresiones regulares para que sólo acepte letras y no números:


midelegado0.cpp (para la columna 0):
Código C++:
Ver original
  1. #include "midelegado0.h"
  2.  
  3. #include <QLineEdit>
  4. #include <QRegExpValidator>
  5.  
  6. MiDelegado0::MiDelegado0(QWidget *parent) : QStyledItemDelegate(parent){}
  7.  
  8. QWidget * MiDelegado0::createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  9. {
  10.     Q_UNUSED (index);
  11.     Q_UNUSED (option);
  12.     QLineEdit* miEditor = new QLineEdit(parent);
  13.     QRegExpValidator* validadorTexto = new QRegExpValidator(QRegExp("[A-Za-z ]{1,20}"),miEditor);
  14.     miEditor->setValidator(validadorTexto);
  15.     return miEditor;
  16. }
  17.  
  18. void MiDelegado0::setEditorData ( QWidget * editor, const QModelIndex & index ) const
  19. {
  20.     QLineEdit* miEditor = static_cast<QLineEdit*>(editor);
  21.     miEditor->setText(index.data().toString());
  22. }
  23.  
  24. void MiDelegado0::setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const
  25. {
  26.     QLineEdit* miEditor = static_cast<QLineEdit*>(editor);
  27.     model->setData(index,miEditor->text());
  28. }
  29.  
  30. void MiDelegado0::updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  31. {
  32.     Q_UNUSED (index);
  33.     editor->setGeometry(option.rect);
  34. }

Luego un delegado para la columna de Sexo. En este caso es un QComboBox y además hago uso del método paint() para poner color a las letras en función del sexo.

midelegado1.cpp

Código C++:
Ver original
  1. #include "midelegado1.h"
  2.  
  3. #include <QComboBox>
  4. #include <QStringList>
  5. #include <QPainter>
  6. #include <QColor>
  7.  
  8. MiDelegado1::MiDelegado1(QWidget *parent) : QStyledItemDelegate(parent)
  9. {
  10.     lista<<"Masculino"<<"Femenino";
  11. }
  12.  
  13.  
  14. QWidget * MiDelegado1::createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  15. {
  16.     Q_UNUSED (index);
  17.     Q_UNUSED (option);
  18.     QComboBox* miCombo = new QComboBox(parent);    
  19.     miCombo->addItems(lista);
  20.     return miCombo;
  21. }
  22.  
  23. void MiDelegado1::setEditorData ( QWidget * editor, const QModelIndex & index ) const
  24. {
  25.     QComboBox* miCombo = static_cast<QComboBox*>(editor);
  26.     miCombo->setCurrentText(index.data().toString());
  27. }
  28.  
  29. void MiDelegado1::setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const
  30. {
  31.     QComboBox* miCombo = static_cast<QComboBox*>(editor);
  32.     model->setData(index,miCombo->currentText());
  33. }
  34.  
  35. void MiDelegado1::updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  36. {
  37.     Q_UNUSED (index);
  38.     editor->setGeometry(option.rect);
  39. }
  40.  
  41. void MiDelegado1::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  42. {
  43.     QColor colorMasculino(Qt::cyan);
  44.     QColor colorFemenino(Qt::magenta);
  45.     painter->save();
  46.     if (index.data().toString()==lista.at(0))
  47.     {
  48.         QPen colorTexto(colorMasculino);
  49.         painter->setPen(colorTexto);
  50.         painter->drawText(option.rect,Qt::AlignCenter,index.data().toString());
  51.  
  52.     }
  53.     else if (index.data().toString()==lista.at(1))
  54.     {
  55.         QPen colorTexto(colorFemenino);
  56.         painter->setPen(colorTexto);
  57.         painter->drawText(option.rect,Qt::AlignCenter,index.data().toString());
  58.     }
  59.     painter->restore();
  60. }

(sigue)
__________________
Mi calculadora en Qt
  #4 (permalink)  
Antiguo 15/03/2015, 10:29
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Otro delegado para la columna de edad. En este caso la idea es usar expresiones regulares para limitar la entrada a números y además colorear las celdas en función de la edad:

midelegado2.cpp

Código C++:
Ver original
  1. #include "midelegado2.h"
  2.  
  3. #include <QLineEdit>
  4. #include <QRegExpValidator>
  5. #include <QPainter>
  6.  
  7. MiDelegado2::MiDelegado2(QWidget *parent) : QStyledItemDelegate(parent) {}
  8.  
  9. QWidget * MiDelegado2::createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  10. {
  11.     Q_UNUSED (index);
  12.     Q_UNUSED (option);
  13.     QLineEdit* miEditor = new QLineEdit(parent);
  14.     QRegExpValidator* validadorEdad = new QRegExpValidator(QRegExp("[0-9]{1,2}"),miEditor);
  15.     miEditor->setValidator(validadorEdad);
  16.     return miEditor;
  17. }
  18.  
  19. void MiDelegado2::setEditorData ( QWidget * editor, const QModelIndex & index ) const
  20. {
  21.     QLineEdit* miEditor = static_cast<QLineEdit*>(editor);
  22.     miEditor->setText(QString::number(index.data().toInt()));
  23. }
  24.  
  25. void MiDelegado2::setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const
  26. {
  27.     QLineEdit* miEditor = static_cast<QLineEdit*>(editor);
  28.     model->setData(index,miEditor->text());
  29. }
  30.  
  31. void MiDelegado2::updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  32. {
  33.     Q_UNUSED (index);
  34.     editor->setGeometry(option.rect);
  35. }
  36.  
  37. void MiDelegado2::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  38. {
  39.     QColor colorInfantil(Qt::green);
  40.     QColor colorMadurez(Qt::yellow);
  41.     QColor colorVejez(Qt::red);
  42.     painter->save();
  43.     if (index.data().toInt()>=0 && index.data().toInt()<25)
  44.     {
  45.         painter->fillRect(option.rect,colorInfantil);
  46.     }
  47.     else if (index.data().toInt()>=25 && index.data().toInt()<65)
  48.     {
  49.         painter->fillRect(option.rect,colorMadurez);
  50.     }
  51.     else if (index.data().toInt()>=65)
  52.     {
  53.         painter->fillRect(option.rect,colorVejez);
  54.     }
  55.     QPen colorTexto(Qt::black);
  56.     painter->setPen(colorTexto);
  57.     painter->drawText(option.rect,Qt::AlignCenter,index.data().toString());
  58.     painter->restore();
  59. }

Y por último, para ensayar con el tema de ver la información en forma de iconos, tengo un campo de estado de ánimo. Y este es el que no funciona:

midelegado3.cpp

Código C++:
Ver original
  1. #include "midelegado3.h"
  2. #include <QComboBox>
  3. #include <QLabel>
  4. #include <QDebug>
  5.  
  6.  
  7. MiDelegado3::MiDelegado3(QWidget *parent) : QStyledItemDelegate(parent) {}
  8.  
  9. QWidget* MiDelegado3::createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const
  10. {
  11.     Q_UNUSED (index);
  12.     Q_UNUSED (option);
  13.     QComboBox* miCombo = new QComboBox(parent);
  14.     QIcon icono1, icono2, icono3;
  15.     icono1.addFile(QStringLiteral(":/iconos/alegre.png"), QSize(), QIcon::Normal, QIcon::Off);
  16.     miCombo->addItem(icono1, "Alegre");
  17.     icono2.addFile(QStringLiteral(":/iconos/serio.png"), QSize(), QIcon::Normal, QIcon::Off);
  18.     miCombo->addItem(icono2, "Serio");
  19.     icono3.addFile(QStringLiteral(":/iconos/triste.png"), QSize(), QIcon::Normal, QIcon::Off);
  20.     miCombo->addItem(icono3, "Triste");
  21.     return miCombo;
  22. }
  23.  
  24. void MiDelegado3::setEditorData (QWidget * editor, const QModelIndex & index) const
  25. {
  26.     QComboBox* miEditor = static_cast<QComboBox*>(editor);
  27.  
  28.     switch (index.data().toInt())
  29.     {
  30.     case 0:
  31.     {
  32.  
  33.         //model->setData( index, miEditor->currentIndex( ) );
  34.         //otro->setPixmap(QPixmap(QString::fromUtf8(":/iconos/alegre.png")));
  35.         break;
  36.     }
  37.     case 1:
  38.     {
  39.         //miEditor->setPixmap(QPixmap(QString::fromUtf8(":/iconos/serio.png")));
  40.         break;
  41.     }
  42.     case 2:
  43.     {
  44.         //miEditor->setPixmap(QPixmap(QString::fromUtf8(":/iconos/triste.png")));
  45.         break;
  46.     }
  47.     default:
  48.         break;
  49.     }
  50. }
  51.  
  52. void MiDelegado3::setModelData (QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
  53. {
  54.     QComboBox* miCombo = static_cast<QComboBox*>(editor);
  55.     model->setData(index,miCombo->currentIndex());
  56. }

(sigue)
__________________
Mi calculadora en Qt
  #5 (permalink)  
Antiguo 15/03/2015, 10:40
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

El caso es que si no se pueden usar diferentes widgets como editores ya está todo dicho.
Era porque el QComboBox del último delegado usa el icono y una leyenda, y mi idea era que una vez seleccionada la opción, solamente apareciera el icono y no la leyenda.


Por cierto...¿como puedo hacer para colgar todo el código, incluidos los ficheros de los iconos?

Esta sería la salida (un poco hortera, pero sirve para lo que se pretendía)
__________________
Mi calculadora en Qt
  #6 (permalink)  
Antiguo 15/03/2015, 14:16
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Cita:
Iniciado por dehm Ver Mensaje
El caso es que si no se pueden usar diferentes widgets como editores ya está todo dicho.
Como editor puedes usar el widget que quieras... QComboBox, QLineEdit, un widget personalizado... Precisamente para poder darte esa flexibilidad, al usar el editor no estándar tienes que sobrecargar estos métodos, para configurar el editor correctamente.

Cita:
Iniciado por dehm Ver Mensaje
Era porque el QComboBox del último delegado usa el icono y una leyenda, y mi idea era que una vez seleccionada la opción, solamente apareciera el icono y no la leyenda.
Si quieres que solo salga el icono al salir de la edición, tienes que tocar el "data" del modelo para que no genere ningún texto en el role "DisplayRole" para la columna correspondiente... únicamente tienes que sacar la imagen en "DecorationRole". No se si me he explicado con claridad.

Tienes que saber diferenciar entre la edición y el visionado normal. Durante el visionado normal únicamente se llama a "data" para extraer la información del modelo. Cuando entras en modo edición, se llama al método para crear el editor y después a setEditorData... en setEditorData tienes que hacer las llamadas que necesites al "data" del modelo para obtener toda la información que necesites. Después se muestra el widget... el usuario modifica y sale de la edición. En ese momento se llama a "setModelData" para recuperar la información del editor y transferirla al modelo, para lo que tienes que llamar a "setData" del modelo. Finalmente se elimina el editor.
  #7 (permalink)  
Antiguo 16/03/2015, 06:39
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Hola eferion:

Disculpa que sea tan pesado.
Bueno, el caso es que efectivamente tenía el concepto equivocado de cómo funcionaban los métodos createEditor() y setEditorData().//mas bien este ultimo

Pero sigo sin tener claro quién se encarga de manejar los iconos.
Voy a ver si consigo esquematizar las cosas:

Por un lado me interesa desacoplar la información de la columna con su icono, porque éste último sólo es una ayuda a la visualización, pero no un dato en sí.
Entonces mi "fuente de datos" no he de tocarla:
Código C++:
Ver original
  1. struct Persona
  2. {
  3.     QString Nombre;
  4.     QString Sexo;
  5.     int Edad;
  6.     eEstado estado;
  7. };

Pero por otra parte, entiendo que es el modelo el que ha de gestionar esos iconos. La verdad es que pensaba que era el propio delegado. Esta parte que me comentas de que el delegado sólo sirve para editar datos....yo pensaba que también es el encargado de mostrarlos :-|

Bueno, pero incorporo esto al modelo:
*.h
Código C++:
Ver original
  1. private:
  2.  
  3.     Persona persona[FILAS];
  4.     QStringList cabecera;
  5.     QIcon iconos[3];

*.cpp
Código C++:
Ver original
  1. MiModelo::MiModelo(QObject *parent) : QAbstractTableModel(parent)
  2. {
  3. ------------------------
  4. iconos[0].addFile(QStringLiteral(":/iconos/alegre.png"), QSize(), QIcon::Normal, QIcon::Off);
  5.     iconos[1].addFile(QStringLiteral(":/iconos/serio.png"), QSize(), QIcon::Normal, QIcon::Off);
  6.     iconos[2].addFile(QStringLiteral(":/iconos/triste.png"), QSize(), QIcon::Normal, QIcon::Off);

Y cuando le sirvo el dato a la vista, hago así en el método data() del modelo:
Código C++:
Ver original
  1. if (index.column()==3)
  2.     {
  3.         if (role == Qt::DisplayRole)
  4.         {
  5.             return iconos[persona[index.row()].estado]; //devuelvo un QIcon
  6.         }
  7.     }

Y ahora, en el delegado hago así:
Código C++:
Ver original
  1. void MiDelegado3::setModelData (QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
  2. {
  3.     QComboBox* miCombo = static_cast<QComboBox*>(editor);
  4.     model->setData(index, miCombo->currentText(), Qt::DisplayRole);
  5. }

Pero esto, además de no funcionar, me obliga a tener duplicadas los arrays de iconos, uno para el modelo y otro para el delegado (para alimentar al QComboBox) y sospecho que no es esa la mejor forma de enfocar el tema.

¿En que me estoy equivocando?

Gracias de nuevo
__________________
Mi calculadora en Qt
  #8 (permalink)  
Antiguo 16/03/2015, 07:45
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Te has liado. Como te comenté, los iconos se establecen en el rol "DecorationRole" no en "DisplayRole":

Código C++:
Ver original
  1. if (index.column()==3)
  2.         {
  3.             if (role == Qt::DecorationRole)
  4.             {
  5.                 return iconos[persona[index.row()].estado]; //devuelvo un QIcon
  6.             }
  7.         }

Por otro lado, entiendo que no es algo idóneo el tener información duplicada. Lo suyo sería crear una clase que almacene los iconos... no se, quizás algo del tipo:

Código C++:
Ver original
  1. class Resources
  2. {
  3.   public:
  4.     static std::vector< QPixmap > GetIcons( );
  5. };

Con un diseño de este tipo, tanto el modelo como el editor pueden acceder a la misma fuente de iconos.

Y, para terminar. Cuando se editan datos en el modelo no se ha de usar "DecorationRole", sino "EditRole". Por supuesto, para que esto funcione, tienes que sobrecargar el método "setData" del modelo para que, dados un index, un QVariant y un rol (EditRole por defecto) sea capaz de modificar la información almacenada en el modelo:

Código C++:
Ver original
  1. bool MiModelo::setData(const QModelIndex & index, const QVariant & value, int role )
  2. {
  3.   bool to_return = false;
  4.  
  5.   if ( index.column( ) == 3 && role == Qt::EditRole )
  6.   {
  7.     // "value", que es un int, te indica el id del icono seleccionado.
  8.     // Aquí tienes que actualizar el elemento correspondiente.
  9.     to_return = true;
  10.   }
  11.  
  12.   return to_return;
  13. }

Bueno, matizo. Si en "setData", cambias "EditRole" por "DisplayRole", entonces podrás guardar cosas usando "DisplayRole", pero no es el comportamiento por defecto. El editor predeterminado intentará modificar tu modelo usando "EditRole", por lo que lo lógico es que te amoldes al uso estándar de la función para evitar problemas (si, por ejemplo, cambias de widget por uno que has comprado).

Un saludo.

Última edición por eferion; 16/03/2015 a las 07:53
  #9 (permalink)  
Antiguo 16/03/2015, 09:35
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Ay disculpa, eferion. Efectivamente, me dijiste Qt::DecorationRole y yo seguía con Qt::DisplayRole.

Eso y el resto de ayuda me han permitido dar por terminado y entendido esta parte de los delegados.

Es verdad que ahora no le puedo dedicar tiempo apenas, pero la verdad es que el tema de Modelos/Vistas/Delegados me está suponiendo un tema muy complejo (empecé a ver cosas el pasado verano). Casi una disciplina propia. Y todavía no he insertado/borrado elementos, ni seleccionado, ni me he metido con el tema de arrastrar y copia/pega....proxymodels, bufff, esto va para largo.

En fin, gracias como siempre...poquito a poco.
Saludos!
__________________
Mi calculadora en Qt
  #10 (permalink)  
Antiguo 16/03/2015, 09:39
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Es cierto que el tema de los modelos es complejo de entender. Pero cuando lo controlas empiezas a ser consciente de lo potente que es :)

Y ya sabes, para cualquier duda aquí estamos.
  #11 (permalink)  
Antiguo 17/03/2015, 14:41
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Bueno, por no abrir un nuevo tema, y aunque este esta solucionado para lo que había preguntado originalmente, lo reciclo y planteo dos dudas relacionadas con los delegados.

1.- ¿Usar delegados es la mejor forma (mas correcta) de formatear un número para que aparezca el punto como separador de miles y la coma como separador de decimales?

y (esta es más compleja)

2.- ¿Qué hago cuando necesito más información que el propio data() para formatear o mostrar un valor? Es decir, en mi caso concreto, yo quiero que el fondo de mi celda de la tabla sea de un color u otro dependiendo del valor en sí, pero también del nivel del grafo en el que se encuentra ese valor. Pero el delegado no tiene información a eso último, sólo al propio valor. Entonces, ¿cómo se puede resolver eso?

Saludos y gracias de nuevo!
__________________
Mi calculadora en Qt
  #12 (permalink)  
Antiguo 18/03/2015, 01:50
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Cita:
Iniciado por dehm Ver Mensaje
1.- ¿Usar delegados es la mejor forma (mas correcta) de formatear un número para que aparezca el punto como separador de miles y la coma como separador de decimales?
Los delegados lo que te permiten es utilizar widgets diferentes al usado por defecto para visualizar (QLabel) / editar(QLineEdit) los datos de la vista.

Si simplemente necesitas que, en visualización, un número aparezca formateado con separador de miles y de decimales, te puedes ahorrar el delegado y conseguir esta salida en el data correspondiente:

Código C++:
Ver original
  1. QVariant Model::data( ... )
  2. {
  3.   if ( model.colum( ) == 0 && role == Qt::DisplayRole )
  4.   {
  5.     // Creo que es esta la secuencia correcta
  6.     return QString( "%L1" ).arg( lista_elements[ model.row( ) ].numero );
  7.   }
  8. }

Cita:
Iniciado por dehm Ver Mensaje
2.- ¿Qué hago cuando necesito más información que el propio data() para formatear o mostrar un valor? Es decir, en mi caso concreto, yo quiero que el fondo de mi celda de la tabla sea de un color u otro dependiendo del valor en sí, pero también del nivel del grafo en el que se encuentra ese valor. Pero el delegado no tiene información a eso último, sólo al propio valor. Entonces, ¿cómo se puede resolver eso?
Los modelos te permiten crear tus propios roles para usos personales. El único requisito es que el identificador de dichos roles sea igual o superior a Qt::UserRole. De esta forma puedes aprovechar toda la lógica programada en el modelo para devolver la información que necesitas:

Código C++:
Ver original
  1. class Model : public QAbstractTableModel
  2. {
  3.   public:
  4.     static const int CustomRole = Qt::UserRole + 1;
  5.  
  6.     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole ) const override
  7.     {
  8.       if ( index.isValid( ) && role == CustomRole )
  9.         return "Test";
  10.  
  11.       return QVariant( );
  12.     }
  13. };
  14.  
  15. int main( )
  16. {
  17.   Model model;
  18.  
  19.   QModelIndex index = model->index( 0, 0 );
  20.   qDebug( ) << model->data( index, Model::CustomRole );
  21. }


Otra posibilidad es aprovechar los QModelIndex. Los modelos tienen un método sobrecargable llamado "index". Este método, junto con "parent" son los encargados de crear el mapeado de datos. Por otro lado, QModelIndex pone a tu disposición un puntero que puedes usar para que apunte a la clase que contiene la información correspondiente a la fila a la que pertenece el index (son muchos conceptos, ahora los aclaro). La idea entonces es sobrecargar "index" y "parent" para componer a mano el mapeo de datos y aprovechar esta sobrecarga para que cada "QModelIndex" apunte a la instancia correcta. Un simple cast sobre ese puntero te permite acceder a toda la información que necesites:

Código C++:
Ver original
  1. struct ModelData
  2. {
  3.   int numero;
  4.   QString texto;
  5. };
  6.  
  7. class Model : public QAbstractTableModel
  8. {
  9.   public:
  10.     QModelIndex index(int row, int column, const QModelIndex& /*parent*/ = QModelIndex()) const override
  11.     {
  12.       if( row >= 0 && row < _data.size( ) && column >= 0 && column < 2 )
  13.         return createIndex( row, column, &_data[ row ] );
  14.       else
  15.         return QAbstractTableModel::index( row, column, parent );
  16.     }
  17.  
  18.     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole ) const override
  19.     {
  20.       if ( index.isValid( ) )
  21.       {
  22.         ModelData* data = reinterpret_cast< ModelData* >( index.internalPointer( ) );
  23.         if ( index.column( ) == 0 )
  24.           return data->numero;
  25.         else
  26.           return data->texto;
  27.       }
  28.  
  29.       return QVariant( );
  30.     }
  31.  
  32.   private:
  33.  
  34.     std::vector< ModelData > _data;
  35. };

Queda de tu parte completar el modelo, no lo he hecho para que quedase clara la idea sobre cómo usar el puntero de QModelIndex.

Al igual que el QModelIndex que recibe "data" tiene su puntero apuntando a un elemento de "_data", el QModelIndex que recibirá el delegado también. Con esto ya deberías disponer de toda la información disponible.
  #13 (permalink)  
Antiguo 18/03/2015, 02:57
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Hola eferion:

Gracias por estar ahí. Aunque he visto tu respuesta (y la entiendo!!!) me pregunto si sería válido éste planteamiento que conseguí hacer ayer después de plantear la duda....quiero decir, funciona pero no sé si es ortodoxo.

La idea es sobrecargar el constructor del delegado, de forma que incorpore un puntero al grafo en cuestión, y poder hacer uso así del mismo.

Esto es lo que he hecho:

El delegado:
Código C++:
Ver original
  1. #ifndef DELEGADOPARANUMERO_H
  2. #define DELEGADOPARANUMERO_H
  3.  
  4. #include <QStyledItemDelegate>
  5.  
  6. class Grafo;
  7.  
  8. class DelegadoParaNumero : public QStyledItemDelegate
  9. {
  10.     Q_OBJECT
  11. public:
  12.     explicit DelegadoParaNumero(Grafo* grafo, QWidget *parent = 0);
  13.  
  14. private:
  15.  
  16.     Grafo* G;
  17.  
  18. protected:
  19.     virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
  20.  
  21. };
  22.  
  23. #endif // DELEGADOPARANUMERO_H

el *.cpp

Código C++:
Ver original
  1. DelegadoParaNumero::DelegadoParaNumero(Grafo* grafo, QWidget *parent) : G(grafo), QStyledItemDelegate(parent) {}
  2.  
  3. void DelegadoParaNumero::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
  4. {
  5.     QColor colorFondo (Qt::cyan);
  6.     painter->save();
  7.     //painter->setPen(colorNumeros);
  8.     if (G->NivelCero())
  9.     {
  10.         painter->fillRect(option.rect,colorFondo);
  11.     }
  12.     painter->drawText(option.rect,Qt::AlignCenter,index.data().toString());
  13.     painter->restore();
  14. }

Perdón, añado el "seteo" de este delegado en la tabla:

Código C++:
Ver original
  1. tabla->setItemDelegateForColumn(0,new DelegadoParaNumero(migrafo));

¿Qué piensas? :-?
__________________
Mi calculadora en Qt

Última edición por dehm; 18/03/2015 a las 03:42 Razón: Añadir información
  #14 (permalink)  
Antiguo 18/03/2015, 04:16
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Depende:
  • Si "Grafo" es un elemento común a todos los registros de la tabla... es una opción.
  • Si, en cambio, "Grafo" depende del registro a editar, lo suyo sería que lo facilitases vía QModelIndex.
  #15 (permalink)  
Antiguo 18/03/2015, 06:10
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Gracias. Super claro!
__________________
Mi calculadora en Qt
  #16 (permalink)  
Antiguo 08/04/2015, 12:13
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Bueno, sigo estirando el chicle con esta pregunta inicial, por no abrir un hilo nuevo.

Ahora la duda que tengo es cómo poder recoger el valor de un ítem de una tabla en forma de icono.

Me explico mejor:

Yo tengo un rango de valores e iconos asociados a ellos.
Entonces, en la función
Código C++:
Ver original
  1. QVariant data (const QModelIndex & index, int role) const;

yo envío el icono a la tabla de esta forma:

Código C++:
Ver original
  1. if (index.column()==3)
  2.     {
  3.         if (role == Qt::DecorationRole)
  4.         {
  5.            return iconos[persona[index.row()].estado];
  6.         }
  7.     }

Pero ahora, cuando voy a definir el valor actual en el editor del delegado, no encuentro la forma de recuperar el valor numérico (en éste caso ese valor sería el índice actual de un QComboBox)

Lo más cerca de la solución que he estado, es enviando no sólo el icono sino también el propio índice:

Código C++:
Ver original
  1. if (index.column()==3)
  2.     {
  3.         if (role == Qt::EditRole)
  4.         {
  5.            return index.row().estado;
  6.         }
  7.     }

Pero claro, eso me muestra el valor junto al icono.

Así que esa es la pregunta....cómo puedo hacer para obtener el valor del índice desde el icono (creo que no hay forma) o como enviar el índice, pero que no se muestre en la tabla.

Saludos y gracias como siempre

Y como parece que hoy es el día de Qt, aprovecho para publicitar este foro, en el que el amigo jc_moj tiene mucho que decir
__________________
Mi calculadora en Qt
  #17 (permalink)  
Antiguo 09/04/2015, 00:07
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Una posible solución puede ser definirte un rol personalizado:

Código C++:
Ver original
  1. // Tu modelo
  2. class [Model] : public QAbstractItemModel
  3. {
  4.   public:
  5.     // Qt te facilita UserRole como punto de partida para tus roles personalizados
  6.     static const int IconIndexRole = Qt::UserRole+ 1;
  7. };

Entonces, el método data lo editas para añadir algo tal que:

Código C++:
Ver original
  1. if (index.column()==3)
  2.         {
  3.             if (role == [Model]::IconIndexRole )
  4.             {
  5.                return index.row().estado;
  6.             }
  7.         }

y ya solo te queda realizar la llamada correcta:

Código C++:
Ver original
  1. int iconIndex = model->data( index, [Model]::IconIndexRole ).toInt( );

¿Por qué esta solución?

Básicamente porque el dato que intentas exportar no es un elemento que puedas incluir en ninguno de los roles ya existentes, o al menos no es deseable hacerlo porque si no va a aparecer en la vista y no es lo que se pretende.

Puedes crear tantos roles personalizados como te de la gana... lo único importante es que fijes como punto de partida Qt::UserRole para evitar que tu rol se solape con alguno propio de Qt.

Un saludo.
  #18 (permalink)  
Antiguo 09/04/2015, 02:37
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Hola eferion:

¿La idea es mandar un "role" que no interprete la tabla, pero que sí me suministre la información que necesito?

Saludos y gracias de nuevo
__________________
Mi calculadora en Qt
  #19 (permalink)  
Antiguo 09/04/2015, 03:12
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Cita:
Iniciado por dehm Ver Mensaje
¿La idea es mandar un "role" que no interprete la tabla, pero que sí me suministre la información que necesito?
Efectivamente. Los roles "estandar" son interpretados por la vista en un momento u otro, luego si necesitas facilitar información que no deseas que sea interpretada te ves obligado a elegir entre una de estas dos opciones:

  • Creas un rol propio
  • Creas un método en el modelo que te permita acceder a esa información.

La primera opción tiene como ventajas las siguientes:
  • Su uso es más estándar
  • Si el "rol" lo defines a nivel global, no necesitas conocer el tipo de modelo al que te conectas
  • Aprovechas toda la infraestructura del modelo de cara a localizar el dato pedido

Y son estas las razones por las que te he recomendado esta solución.

Un saludo
  #20 (permalink)  
Antiguo 09/04/2015, 03:26
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. Delegados. Usar diferente widget para editar y para mostrar

Pues nada, tema RESOLUCIONADO y gracias de nuevo
__________________
Mi calculadora en Qt

Etiquetas: funcion, int, programa
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 17:00.