Project

General

Profile

Wt::WStandardItem how to set id?

Added by Tristan Hooper about 14 years ago

Hi,

I have a WTableView which uses a WStandardItemModel. WStandardItemModel creates rows by passing vectors of type WStandardItem.

The problem is that each WStandardItem is assigned a random id. Is there a way to specify an id similar to WWdiget::SetId()?

Tristan


Replies (10)

RE: Wt::WStandardItem how to set id? - Added by Wim Dumon about 14 years ago

Not that I am aware of. I don't even think that that ID is currently used; what do you want to do with it?

BR,

Wim.

RE: Wt::WStandardItem how to set id? - Added by Tristan Hooper about 14 years ago

I need controls to have a unique Id for automated testing of my application/webpage. (using [[http://seleniumhq.org/]])

Fortunately most controls inherit from WWidget and therefore have the setId() function.

In this case, the table has a list of items with various properties. The first item of a row is a WStandardItem with setCheckable(true). I can't check/uncheck this because the WStandardItem has a random id "c_XXXXXXX"

As I insert additional rows, the new id's of the new row get assigned different values "c_YYYYYYY". This led me to believe they are being used and assigned an id similar to the way other controls are.

RE: Wt::WStandardItem how to set id? - Added by Koen Deforche about 14 years ago

Hey,

Now it makes sense!

A standard item does not map one-on-one with a widget though, since a model may be shared among multiple views. The place where such an option should be implemented is a WItemDelegate. By specializing WItemDelegate, and using that specialized item delegate for your view, you will be able to call setId() on the widget. But only for the simple case (only a DisplayRole) this will be a simple WText. In other cases, the widget is more complex to reflect checkboxes, icons, anchors, etc...

Although it is not documented, you can find the WText which displays the DisplayRole value using widget->find("t") (i.e. it's got an object name of "t"). This is an implementation detail, but, I believe fairly stable, at least for testing purposes.

Likewise, the checkbox can be found using widget->find("c"), an icon using find("i"), etc...

Regards,

koen

RE: Wt::WStandardItem how to set id? - Added by Стойчо Стефанов Stoycho Stefanov over 13 years ago

Hi Koen,

I'm facing a similar problem as that of Tristan. I would like to be able to set the IDs (all the same) of all items in a given column of a WTableView. I'm not sure that I really understand what you meant with that:

The place where such an option should be implemented is a WItemDelegate. By specializing WItemDelegate, and using that specialized item delegate for your view, you will be able to call setId() on the widget.

Could you explain it more comprehensive or/and give some example how to do it, please!

Regards,

Stoycho

RE: Wt::WStandardItem how to set id? - Added by Koen Deforche over 13 years ago

Hey,

WAbstractItemView::setItemDelegate() allows you to set a new item delegate.

The standard implementation is WItemDelegate. You can set a specialized WItemDelegate and specialize its update() method on the result to set the ID.

Regards,

koen

RE: Wt::WStandardItem how to set id? - Added by Tristan Hooper over 13 years ago

In the following example I override WItemDelegate so I can have clickable WStandardItem's in my WTableView which will popup dialog boxes instead of normal WAnchor internal or url navigation. Slightly different than what was asked above, but it still allows you to setId() for automation control

in .h file

class cWtDialogItemDelegate : public Wt::WItemDelegate
{
public:
    cWtDialogItemDelegate(Wt::WObject *parent = 0);
    Wt::WWidget *update(Wt::WWidget *widget, const Wt::WModelIndex& index, Wt::WFlags<Wt::ViewItemRenderFlag> flags);

    void setId(std::string newId);

private:
    void onClicked(std::string designations);
    void initDialog(std::string designations, Wt::WDialog *dialog);

    std::string mId;                         //!< id for anchor
};

in .cpp file, override WItemDelegate::update()

WWidget *cWtDialogItemDelegate::update(WWidget *widget, const WModelIndex& index, WFlags<ViewItemRenderFlag> flags)
{
    //WItemDelegate::update() must be called first incase widget is NULL
    WWidget *result = WItemDelegate::update(widget, index, flags);

    //get designation data stored in 'UserRole'
    std::string text = asString(index.data(DisplayRole)).toUTF8();
    boost::any data = index.data(UserRole);

    if (data.empty() == false)
    {
        //cast to interactive widget, so we can connect up onClicked signal, and set idW
        Wt::WInteractWidget *iw = dynamic_cast< Wt::WInteractWidget* >( result );
        if (iw != NULL)
        {
            //change control id, so we could automate a click on the anchor
            iw->setId(mId + text);

            //add event on anchor that is clicked
            iw->mouseWentUp().connect(boost::bind(&cWtDialogItemDelegate::onClicked, this, boost::any_cast< std::string >(data)));
        }
    }

    return result;
}

RE: Wt::WStandardItem how to set id? - Added by Стойчо Стефанов Stoycho Stefanov over 13 years ago

Hi Koen.

Hi Tristan,

your implementation is good and now I can set IDs to the items too, but I have to solve one more problem together with the previous one.

I'll try now to sketch my task. I need a table where values of different types are listed (e.g., boolean and float values). That is easy to achieve as long as the JavaScriprt is enabled, but I need to edit the table values without JavaScript as well. For the boolean values I use the CheckStateRole and the ItemIsUserCheckable flag, but I cannot check the check boxes without JS. If I specialize my own ItemDelegate which uses WChechBox, I can click and check them but I cannot get access to the check box state in order to update my model or do something else with the new value. I didn't yet implement the createEditor() and setEditState() members, but I'm afraid that some of the signals needed to implement them wouldn't emit, because I saw that the default edit mode is toggled without JS, but I cannot type anything in the LineEdit.

It would be easier for me if I could get access to the rendered item over the WTableView object, for example with something like this:

tableView_->itemDelegate(tableModel_->index(i,j))->renderedWidget();

where renderedWidget() have to return a WWidget* pointing to the widget rendered by the WTableView. One Problem here is that the Delegates are common for the hole table or at least for the hole column.

@Koen Deforche: Do you recommend to use the WTableView when JavaScript is disabled or I should simply use WTable instead.

Thanks in advance

Regards,

Stoycho

RE: Wt::WStandardItem how to set id? - Added by Koen Deforche over 13 years ago

Hey,

Without JavaScript, the inline editing is indeed a bit problematic. Perhaps it is better to provide an alternative way of editing your data when JavaScript is not available, e.g. using a dialog ?

It seems generally useful to be able to access the 'rendered' widget for an index (if it is rendered), and it does not even involve the item delegate per se (although you would expect the item delegate to be involved in dealing with this widget). Although I don't really understand how that solves the problem at hand, I've added this method to WAbstractItemView: itemWidget(const WModelIndex&).

Regards,

koen

RE: Wt::WStandardItem how to set id? - Added by Стойчо Стефанов Stoycho Stefanov over 13 years ago

Hi Koen,

WAbstractItemView::itemWidget(const WModelIndex&)

seems to be the thing that could help me.

In which Wt git you added the method, I didn't understand?

Actually, I solved my Problem as follows:

Wt::WWidget* floatItemDelegate::createEditor(const Wt::WModelIndex& index, WFlags<ViewItemRenderFlag> flags) const
{
    Wt::WContainerWidget *result = new Wt::WContainerWidget();
    result->setSelectable(true);

    boost::any data = index.data(EditRole);
    WLineEdit *edit = new WLineEdit();
    WPushButton *setBtn = new WPushButton("set");
    if (data.empty() == false)
    {
        edit->setText(asString(data));
    }
    setBtn->clicked().connect(boost::bind(&floatItemDelegate::doCloseEditor, this, result, true));

    if (flags & RenderFocused)
        edit->setFocus();
// !!!!!!!!!!! this order is crucial !!!!!!!
    result->addWidget(setBtn); 
    result->addWidget(edit);
    return result;
}
void floatItemDelegate::doCloseEditor(Wt::WWidget *editor, bool save) const
{
    closeEditor().emit(editor, save);
}
boost::any floatItemDelegate::editState(Wt::WWidget *editor) const
{
    Wt::WContainerWidget *w = dynamic_cast<Wt::WContainerWidget *>(editor);
    Wt::WLineEdit *lineEdit = dynamic_cast<Wt::WLineEdit *>(w->widget(1));
    return boost::any(lineEdit->text());
}
void floatItemDelegate::setEditState(Wt::WWidget *editor, const boost::any& value) const
{
        Wt::WContainerWidget *w = dynamic_cast<Wt::WContainerWidget *>(editor);
    Wt::WLineEdit *lineEdit = dynamic_cast<Wt::WLineEdit *>(w->widget(1));

    lineEdit->setText(boost::any_cast<Wt::WString>(value));
}

As the comment (in the definition of floatItemDelegate::createEditor ) says, it is crucial to add first the button and than the lineEdit. Otherwise the lineEdit is covered by some other widget and (I'm not sure that is covered indeed or just cannot be selected, but ) cannot select it and type within it. I didn't exterminate yet whether there are any the dependences between the column width and the common width of the button and the lineWdit (see the figure), but as long as the widgets from the edit mode are placed on the line below the original one everything works fine.

regards,

Stoycho

RE: Wt::WStandardItem how to set id? - Added by Koen Deforche over 13 years ago

Hey,

I've pushed this to the remote git now (github or webtoolkit.eu). Untested, yet!

Regards,

koen

    (1-10/10)