Results 1 to 14 of 14

Thread: [PyQT] QDataWidgetMapper and Custom Widget

  1. #1
    Join Date
    Jan 2015
    Posts
    11
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default [PyQT] QDataWidgetMapper and Custom Widget

    Hi all,

    I'm trying to implement a treeview for my application and a qdatawidgetmapper to map the model data into a properties widget to facilitate the modification of the model data. So far, everything works pretty well (thanks to the M. Summerfield books). I can use easily QLineEdit, QSpinBox and QComboBox widgets in the properties widget. Now, I would like to add a custom widget (a "color" widget) in this properties wigdet. Basically, this custom widget is a QLineEdit which can not be manually edited (readOnly mode activated) and a QPushButton that opens a QColorDialog instance when the button is clicked. Basically, I choose a color from the QColorDialog instance, and the name of the color is programmatically written into the QLineEdit. But I don't know how to do the mapping between the model and the properties widget (both in vizualization and edition). I tried few things I found on the internet, but nothing worked and I'm stucked (the examples on the internet are not numerous).

    As an example would probably be clearer for you to understand my problemn, I've added a simple example in attachement of this message. The file contains several classes:
    - ProjectNode : basic class which represents the node of my treeview. Contains 2 attributes used for the mapping onto the properties widget : name and color
    - MyModel : custom model derived from QAbstractItemModel
    - myTreeView : custom treeview
    - PropertiesEditor : widget used to contain the data of the model (name and color)
    - MyTreeWidget : widget that combine the myTreeView and my Properties Editor
    - ColorWidget : custom widget evoked previously

    So my problem is : how to map the data of a model onto a custom widget?

    Any help would be greatly appreciated! Thanks in advance for your time

    Vincent
    Attached Files Attached Files

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    Your color widget needs a property that the widget mapper can write to and read from.
    See http://doc.qt.io/qt-5/properties.html for properties in general.

    Then you either mark it as USER or specify the property's name with the addMapping() overload that takes three arguments.

    Cheers,
    _

  3. #3
    Join Date
    Jan 2015
    Posts
    11
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    Thank you very much for your time and reactivity.

    I tried what you just said by adding in the mapping section:
    Qt Code:
    1. self.dataMapper.addMapping(self.colorWidget, 1, "color")
    To copy to clipboard, switch view to plain text mode 

    and the"color" property is defined in the ColorWidget class using the QtCore.pyqtProperty class according to:

    Qt Code:
    1. def getColor(self):
    2. return self._color
    3.  
    4. def setColor(self, newColor):
    5. self._color = newColor
    6. self.ldtColor.setText(newColor)
    7. self.updateIcon()
    8.  
    9. color = pyqtProperty(str, getColor, setColor)
    To copy to clipboard, switch view to plain text mode 

    However, this does not seem to solve the modification of the model data. If I navigate through my treeview items, I always get the initial value (the new value is not stored in the model). How can I solve this problem? By emitting a signal when the setColor method is called? If so, how can the QDataWidgetMapper be aware of this?

    Once again, thank you for your time.

    Cheers,

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    Your property might need a NOTIFY signal to indicate that the value has changed.

    Cheers,
    _

  5. #5
    Join Date
    Jan 2015
    Posts
    11
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    I modified the ColorWidget as follows:

    Qt Code:
    1. class ColorWidget(QWidget):
    2. sigDataChanged = pyqtSignal()
    3. def __init__(self, color=None):
    4. QWidget.__init__(self)
    5.  
    6. if color is not None:
    7. self._color = color
    8. else:
    9. self._color = "#ff0000"
    10. self.ldtColor = QLineEdit(self.color)
    11. self.ldtColor.setReadOnly(True)
    12. self.btnColor = QPushButton()
    13. pixmap = QPixmap(24, 24)
    14. pixmap.fill(QColor(self.color))
    15. icon = QIcon(pixmap)
    16. self.btnColor.setIcon(icon)
    17. self.btnColor.setIconSize(QSize(24, 24))
    18. self.btnColor.setFixedWidth(24)
    19. self.btnColor.setFixedHeight(24)
    20. layout = QHBoxLayout()
    21. layout.addWidget(self.ldtColor)
    22. layout.addWidget(self.btnColor)
    23. self.setLayout(layout)
    24. self.setContentsMargins(0, 0, 0, 0)
    25. self.layout().setContentsMargins(0, 0, 0, 0)
    26. self.btnColor.clicked.connect(self.changeColor)
    27.  
    28. def changeColor(self):
    29. colorInit = QColor()
    30. print(self._color)
    31. colorInit.setNamedColor(self._color)
    32. color = QColorDialog.getColor(colorInit, self, "Select a color")
    33. if color.isValid():
    34. self.ldtColor.setText(color.name())
    35. self.setColor(color.name())
    36.  
    37. def updateIcon(self):
    38. pixmap = QPixmap(24, 24)
    39. newcolor = QColor()
    40. newcolor.setNamedColor(self._color)
    41. pixmap.fill(newcolor)
    42. icon = QIcon(pixmap)
    43. self.btnColor.setIcon(icon)
    44.  
    45. def getColor(self):
    46. return self._color
    47.  
    48. def setColor(self, newColor):
    49. self._color = newColor
    50. self.ldtColor.setText(newColor)
    51. self.updateIcon()
    52. self.sigDataChanged.emit()
    53.  
    54. color = pyqtProperty(str, fget=getColor, fset=setColor, notify=sigDataChanged)
    To copy to clipboard, switch view to plain text mode 

    Basically, I just added a pyqtSignal that is emitted when the color has changed. However, this modification does not solve the problem: this modification is not stored into the model. If I read the documentation of pyqtProperty (http://pyqt.sourceforge.net/Docs/PyQ...roperties.html), I can read:

    notify – the optional unbound notify signal. It is ignored by Python.

    What does it means: whatever its value, pyqt will not interpret it? Therefore, can I solve my problem?

    Thanks for your time, it is greatly appreciated!

    Cheers,

    Vincent

  6. #6
    Join Date
    Jan 2015
    Posts
    11
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    Hi all,

    I found a way to circumvent the problem by mapping the model to the QLineEdit widget of my custom but I do not know how to proceed.

    The QLineEdit is set in readOnly mode only and its content is programmatically modified. What signal needs to be emitted to tell the QDataWidgetMapper the content has been modified? Because, when a model is mapped on a QLineEdit, everything is automatic and I need to understand hows it works to be able to reproduce its behaviour.

    Cheers,

    Vincent

  7. #7
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    I played a bit around and what seems to work is to connect the colorwidget's change signal to the mapper's submit slot.

    Cheers,
    _

  8. #8
    Join Date
    Jan 2015
    Posts
    11
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    Could you be more precise or ideally write the piece of code you mentionned because I do not see what you mean.

    Cheers,

    Vincent

  9. #9
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    I haven't done any PyQt programming myself, but maybe something like
    Qt Code:
    1. self.dataMapper.addMapping(self.colorWidget, 1, "color")
    2. self.colorWidget.sigDataChanged.connect(self.dataMapper.submit);
    To copy to clipboard, switch view to plain text mode 

    Cheers,
    _

  10. #10
    Join Date
    Jan 2015
    Posts
    11
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    I did not manage to make it working properly. By changing the policy of the dataMapper to Manual, I get plenty of errors. But I may not use the method correctly. I just did something like:
    Qt Code:
    1. self.dataMapper.submit()
    To copy to clipboard, switch view to plain text mode 
    but it is not working.

    It tried few other things and I noticed something which appear very strange to me. When I change the color attribute of my colorWidget with the QColorDialog, I then write the name of the color in the QLineEdit and modify the icon of the QPushButton. If I change the selected object within my treeView, the change is not stored in the model (we already knew that). However, if I set the focus on the QLineEdit and I press manually enter (to force the widget to emit the returnPressed signal), then the color name is stored into the model (the setData method is called). I tried to reproduce programmaticaly this behaviour by changing the focus on the QLineEdit and by emitting manually the returnPressed signal, but it does not solve my problem. I still have to interact with my widget to force the setData method to be called. Strange isn't it? I start to feel really dumb. This problem should have a simple solution, but I don't find it.

    In attachment the last version of the example (it may be clearer for you to directly test the code).

    Any help would be highly welcomed!

    Thanks in advance for your time.

    Cheers,

    Vincent
    Attached Files Attached Files

  11. #11
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    So you connect to a local slot and that one calls submit(), should work as well.

    Is the slot being invoked?

    it definitely works in C++. I have a color editing widget and connect its signal to the datawidgetmapper's submit() slot and whenever I change color, the model is update accordingly.

    Qt Code:
    1. #ifndef COLOREDITOR_H
    2. #define COLOREDITOR_H
    3.  
    4. #include <QColor>
    5. #include <QWidget>
    6.  
    7. class QLineEdit;
    8.  
    9. class ColorEditor : public QWidget
    10. {
    11. Q_OBJECT
    12. Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    13.  
    14. public:
    15. explicit ColorEditor(QWidget *parent = 0);
    16.  
    17. QColor color() const;
    18.  
    19. signals:
    20. void colorChanged(const QColor &color);
    21.  
    22. public slots:
    23. void setColor(const QColor &color);
    24.  
    25. private:
    26. QColor m_color;
    27. QLineEdit *m_lineEdit;
    28.  
    29. private slots:
    30. void onButtonClicked();
    31. };
    32.  
    33. #endif // COLOREDITOR_H
    To copy to clipboard, switch view to plain text mode 
    Qt Code:
    1. #include "coloreditor.h"
    2.  
    3. #include <QColorDialog>
    4. #include <QHBoxLayout>
    5. #include <QLineEdit>
    6. #include <QPushButton>
    7.  
    8. ColorEditor::ColorEditor(QWidget *parent)
    9. : QWidget(parent)
    10. {
    11. QHBoxLayout *hbox = new QHBoxLayout(this);
    12.  
    13. m_lineEdit = new QLineEdit(this);
    14. hbox->addWidget(m_lineEdit);
    15.  
    16. QPushButton *button = new QPushButton("...", this);
    17. hbox->addWidget(button);
    18. connect(button, SIGNAL(clicked()), this, SLOT(onButtonClicked()));
    19. }
    20.  
    21. QColor ColorEditor::color() const
    22. {
    23. return m_color;
    24. }
    25.  
    26. void ColorEditor::setColor(const QColor &color)
    27. {
    28. if (color == m_color) {
    29. return;
    30. }
    31.  
    32. m_color = color;
    33. m_lineEdit->setText(m_color.name());
    34.  
    35. emit colorChanged(color);
    36. }
    37.  
    38. void ColorEditor::onButtonClicked()
    39. {
    40. const QColor color = QColorDialog::getColor(m_color, this);
    41. if (!color.isValid()) {
    42. return;
    43. }
    44.  
    45. setColor(color);
    46. }
    To copy to clipboard, switch view to plain text mode 
    Qt Code:
    1. ui->tableView->setModel(model);
    2.  
    3. QDataWidgetMapper *mapper = new QDataWidgetMapper(this);
    4. mapper->setModel(model);
    5. mapper->addMapping(ui->name, 0);
    6. mapper->addMapping(ui->color, 1, "color");
    7. mapper->toFirst();
    8.  
    9. connect(ui->color, SIGNAL(colorChanged(QColor)), mapper, SLOT(submit()));
    To copy to clipboard, switch view to plain text mode 

    Cheers,
    _

  12. #12
    Join Date
    Jan 2015
    Posts
    11
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    I finally managed to make it working! Your code is exactly the same as I tried before my last post and I did not manage to make it working because of a recursivity problem (the sigDataChanged signal was directly emitted in the setColor method).

    I had to change a little bit the code :
    Qt Code:
    1. class ColorWidget(QWidget):
    2. sigDataChanged = pyqtSignal(str)
    3. def __init__(self, color=None):
    4. some python code here
    5. self.btnColor.clicked.connect(self.changeColor)
    6.  
    7. def changeColor(self):
    8. colorInit = QColor()
    9. colorInit.setNamedColor(self._color)
    10. color = QColorDialog.getColor(colorInit, self, "Select a color")
    11. if color.isValid():
    12. self.ldtColor.setText(color.name())
    13. self.setColor(color.name())
    14. self.sigDataChanged.emit(self._color)
    15.  
    16. def updateIcon(self):
    17. pixmap = QPixmap(24, 24)
    18. newcolor = QColor()
    19. newcolor.setNamedColor(self._color)
    20. pixmap.fill(newcolor)
    21. icon = QIcon(pixmap)
    22. self.btnColor.setIcon(icon)
    23.  
    24. def getColor(self):
    25. return self._color
    26.  
    27. def setColor(self, newColor):
    28. self._color = newColor
    29. self.ldtColor.setText(newColor)
    30. self.updateIcon()
    31.  
    32. color = pyqtProperty(str, fget=getColor, fset=setColor)
    To copy to clipboard, switch view to plain text mode 
    I just emit the sigDataChanged signal after the setColor method has been called. Finally, I update the model by connecting this signal to the submit slot of the QDataWidgetMapper in the appropriate PropertiesEditor class (as you mentionned).
    Qt Code:
    1. self.colorWidget.sigDataChanged.connect(self.dataMapper.submit)
    To copy to clipboard, switch view to plain text mode 

    Problem solved. I close the thread.

    Thank you very much anda_skoa for your time and help .

    Cheers,

    Vincent

  13. #13
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    Quote Originally Posted by Vincent Le Saux View Post
    I finally managed to make it working! Your code is exactly the same as I tried before my last post and I did not manage to make it working because of a recursivity problem (the sigDataChanged signal was directly emitted in the setColor method).
    My guess would be that this is a problem caused by not checking for actual change.

    I do that in setColor(), so the signal is only emitted if the color actually changes.

    But maybe PyQt behaves differently in some other aspect.

    Cheers,
    _

  14. #14
    Join Date
    Jan 2015
    Posts
    11
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: [PyQT] QDataWidgetMapper and Custom Widget

    You are definitely right! I just check for changes in the setColor method, and the recursivity problem disappears.

    PyQt and Qt behaves exactly the same way on this aspect.

    Once again, thank you!

    Cheers,

    Vincent

Similar Threads

  1. QDataWidgetMapper and custom widget - read value
    By Hostel in forum Qt Programming
    Replies: 3
    Last Post: 11th May 2013, 13:07
  2. Using QDataWidgetMapper with custom types
    By Zanyinj in forum Qt Programming
    Replies: 2
    Last Post: 25th August 2011, 10:42
  3. [PyQt] Creating custom widget
    By asik in forum Newbie
    Replies: 1
    Last Post: 2nd December 2010, 15:33
  4. Custom QLineEdit to store NULL with QDataWidgetMapper
    By certqt in forum Qt Programming
    Replies: 3
    Last Post: 9th November 2010, 14:15
  5. Custom Widgets to PyQt
    By prashant in forum Qt Programming
    Replies: 0
    Last Post: 24th September 2009, 16:17

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.