Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt GUI _ QTableWidget, Как програмно реализовать перетаскивание?

Автор: Danila_Bagrofff 16.4.2009, 12:07

Есть проблемка: есть 3 виджета:

QTableWidget *tbl = new QTableWidget(10,10,this);
QFrame *frm = new QFrame(this);
QWidget *wdg = new QWidget(frm);

QHBoxLayout *hbl = new QHBoxLayout(frame);
hbl->insertWidget(wdg);

//затем в процессе программы
hbl->removeWidget(wdg);
wdg->setParent(tbl);
tbl->setCellWidget(0,0, wdg);


в процессе программы wdg должен перемещаться между двумя виджетами. Соответственно менять родителя. Лэаут сам тоже устанавливает виджету родителя. То есть у wgt становится родителем frm после команды insertWidget().
есть команда setParent(). Но при смене родителя она ругается.
Если мы перемещаем wdg возникает ошибка:

Цитата
X Error: BadWindow (invalid Window parameter) 3
Major opcode: 25 (X_SendEvent)
Resourse id: .....


Как тогда правильно перемещать виджет между другими?


-----------
Отделено от темы: http://www.forum.crossplatform.ru/index.php?showtopic=2577

Автор: igor_bogomolov 16.4.2009, 13:44

Тут проблема на самом деле в другом.
Дело в том, что один и тот же виджет перемещается между фреймом и ячейкой таблицы. В таблицу виджет устанавливается через setCellWidget.
Если заглянуть в исходники данной функции, докопаемся до следущего кода.

Раскрывающийся текст
void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget)
{
    Q_D(QAbstractItemView);
    if (!d->isIndexValid(index))
        return;
    if (QWidget *oldWidget = indexWidget(index)) {
        d->removeEditor(oldWidget);
        oldWidget->deleteLater();
    }
    if (widget) {
        widget->setParent(viewport());
        d->persistent.insert(widget);
        d->addEditor(index, widget, true);
        widget->show();
        if (!d->delayedPendingLayout)
            widget->setGeometry(visualRect(index));
        dataChanged(index, index); // update the geometry
    }
}


Обратите внимание на код. Когда вставляется виджет, сначало происходит удаление старого. При этом не происходит никакой проверки.
Т.о. образом при повторном вставлении виджета в ячейку, модель ее просто удаляет.
Вобщемто недоработка на мой взгляд.

Обойти проблему можно, если только каждый раз создавать новый виджет. Что не есть хорошо. ИМХО.

Автор: igor_bogomolov 16.4.2009, 14:11

Цитата(Litkevich Yuriy @ 16.4.2009, 15:02) *
Если нужно вставлять в ячейку, то устанавливать родителя не надо

Можно и не устанавливать самому. Родидель установтся автоматически.
Цитата
widget->setParent(viewport());

Смотри мое сообщение выше :rolleyes:

Автор: Litkevich Yuriy 16.4.2009, 14:53

igor_bogomolov, тут похоже человеку нужно не простая смена родителя, помоему речь о Перетаскивании, соответственно тему нужно разделять.

Автор: igor_bogomolov 16.4.2009, 14:56

Судя по его коду, он делает именно смену родителя. Логику нужно менять, таким образом добиться нужного эфекта не получится.
Но тему разделить наверно стОит :)

Автор: Danila_Bagrofff 17.4.2009, 7:19

Да, я хочу перенести виджет, чтобы не дублировать.

Сделал очень простой пример:

Раскрывающийся текст

DMainDialog::DMainDialog(QWidget *parent) : QDialog(parent)
{
vbl = new QVBoxLayout(this);
tbl = new QTableWidget(4,4,this);
tbl->setMinimumSize(100,100);
tbl2 = new QTableWidget(4,4,this);
tbl2->setMinimumSize(100,100);
pb = new QPushButton("push", this);
vbl->addWidget(tbl);
vbl->addWidget(tbl2);
vbl->addWidget(pb);

lbl = new QLabel("label", this);
vbl->addWidget(lbl);

connect(pb, SIGNAL(clicked()), SLOT(clickPB()));
i =0;
}

void DMainDialog::clickPB()
{
i++;
if(i%2)
tbl->setCellWidget(0,0,lbl);
else
tbl2->setCellWidget(0,0,lbl);
}


Если надо, могу скомпиленный вариант дать.
Смысл в том, что после пары перемещений лэйбл вообще пропадает!!!
И происходит ошибка сегментирования.

Это бага Qt? Нужно перед новым размещением создавать дубликат обязательно?

Автор: igor_bogomolov 17.4.2009, 8:20

Да елы палы. Ты вообще читаешь что тебе отвечают. Я тебе вчера сразу ответил что происходит в данной ситуации.

Цитата(igor_bogomolov @ 16.4.2009, 14:44) *
Обратите внимание на код. Когда вставляется виджет, сначало происходит удаление старого. При этом не происходит никакой проверки.
Т.о. образом при повторном вставлении виджета в ячейку, модель ее просто удаляет.
Вобщемто недоработка на мой взгляд.
Читай мое сообщение выше.

Автор: Litkevich Yuriy 17.4.2009, 9:40

Цитата(Danila_Bagrofff @ 17.4.2009, 11:19) *
Это бага Qt? Нужно перед новым размещением создавать дубликат обязательно?
дело в том, что QTableWidget, предназанчен для довольно примитивных операций над ним. Поэтому он сам удаляет свои элементы (Игорь уже и исходник класса показал)

Автор: igor_bogomolov 17.4.2009, 10:38

Цитата(Litkevich Yuriy @ 17.4.2009, 10:40) *
QTableWidget, предназанчен для довольно примитивных операций над ним. Поэтому он сам удаляет свои элементы
Вставка виджета происходит на уровне QAbstractItemView методом setIndexWidget. При этом, данный метод не является виртуальным, и переопределить его не получится.

На мой взгляд имеем недоработку(а возможно и баг), т.к. не происходит проверки вставляемого виджета с предыдущим. Предыдущий виджет просто удаляется. Видимо так сделанно из-за того, что копирование виджетов запрещено. Хотя, на мой взгляд, они должны были предусмотреть такую возможность, что бы защититься от лишних ошибок, в результаве повторного вставления одного и того же виджета в ячейку. Это с одной стороны ошибка программиста, но и недоработка библиотеки, в крайнем случае описания данного метода.

Так что можно и отрапортовать о баге, посмотрим что ответят. Как считаете, стОит?

ИМХО.

Автор: Danila_Bagrofff 17.4.2009, 12:32

igor_bogomolov, вы уж извините =) Просто я искал разные пути решения =) И игрался по-всякому, стараясь как-нить обойти =) не получилось.


В итоге, создаю копию нужного мне виджета и уже его размещаю. Увы, видимо, иначе пока сделать нельзя.
Спасибо =)

И не ругайтесь сильно =) не каждый раз натыкаешься на бажки кутэ =)

Автор: igor_bogomolov 22.4.2009, 20:51

По этой теме тоже баг репорт написал. Походу не приняли. Видимо из-за моего английского, не поняли что я имею ввиду :(

Автор: igor_bogomolov 25.4.2009, 0:07

Привет всем.
Сообщением выше я писал, ято отправлял баг репорт по данной теме. Во какой ответ я получил.

Цитата
Hi Igor,
Thanks for reporting this issue.
The code seems to show that this particular case has been omitted, which I agree, could lead to a weird behaviour, even though it is definitely not critical.
However, I tried with no luck reproducing the issue, I attached the test I tried with.
If you either get a FAIL! with this test case, or can provide me with a little compilable example that reproduces the problem, I could file a bug report for that problem.

Моих знаний английского не хватает, что бы до конца понять ответ.
Пробовал перевести Google-ом. Они просят что ли написать небольшой тестовый пример воспроизводящий баг?

Заранее спасибо за помощь :)

Автор: Litkevich Yuriy 25.4.2009, 0:54

Перевод:

Цитата
Привет Игорь,
Спсибо за сообщение об этой проблеме.
Код, как (мне) кажется, показывает, что этот особый случай был опущен (не принят во внимание), с которым я согласен, может привести к неожиданному поведению, даже несмотря на то, что это некритично.
Тем не мение, я пытался безуспешно воспроизвести проблему, я прикрепил тест, который я проводил.
Если выполучите FAIL! во время этого теста, или сможете предоставить мне маленький компилируемый пример, который воспроизводит проблему, я могу подать сообщение об ошибке для этой проблемы.

Автор: igor_bogomolov 25.4.2009, 1:01

Спасибо, Юрий.
Можно еще совет спросить. Я может сейчас, может за выходные, накидаю тестовый примерчик. Как дальше поступить, создать новый баг репорт или ответить на полученное письмо, и там прикрепить тест?

Автор: Litkevich Yuriy 25.4.2009, 2:12

Цитата(igor_bogomolov @ 25.4.2009, 5:01) *
или ответить на полученное письмо, и там прикрепить тест?
или, тебя так и просят.

Автор: igor_bogomolov 27.4.2009, 17:02

Вот ответ который я получил

Цитата
Hi Igor,
Thanks for the test. I am not sure yet why I can't reproduce it with my testcase but yours worked. I've created a task for the development team who will look into fixing
this for a future release.
You can track the status of this entry in the Task Tracker with ID 252253.

See:
http://www.qtsoftware.com/developer/task-tracker/index_html?method=entry&id=252253


Из переписки встал следущий вопрос. В первом письме от тех поддержки я получил следущий код для проверки бага
test.cpp
#include <QtTest/QtTest>
#include <QTreeView>
#include <QStandardItemModel>
#include <QLineEdit>
#include <QApplication>
class Test : public QObject{
      Q_OBJECT

private slots:

      void testSetIndexWidget(){
              QLineEdit *lineEdit = new QLineEdit();
              QTreeView itemView;
              QStandardItemModel model;
              itemView.show();
              model.appendRow(new QStandardItem("an item"));
              itemView.setModel(&model);

              itemView.setIndexWidget(model.index(0,0),lineEdit);
              QApplication::processEvents();

              QVERIFY(itemView.indexWidget(model.index(0,0)));
              QCOMPARE(itemView.indexWidget(model.index(0,0)),lineEdit );

              itemView.setIndexWidget(model.index(0,0),lineEdit);
              QApplication::processEvents();

              QVERIFY(itemView.indexWidget(model.index(0,0)));
              QCOMPARE(itemView.indexWidget(model.index(0,0)),lineEdit );
      }
};

QTEST_MAIN(Test)
#include "test.moc"

testcase.pro
CONFIG+=teambuilder
TEMPLATE = app
TARGET = testcase
DEPENDPATH += .
INCLUDEPATH += .

# Input
SOURCES += test.cpp
CONFIG += qt warn_on debug  create_prl link_prl
QT += testlib



Внутри setIndexWidget для lineEdit у нас вызывается deleteLater. Далее делается QApplication::processEvents() для того чтобы deleteLater отработал. Тем не менее удаления не происходит. Я пробовал делать разные тесты, но положительного результата так и не добился. Кто знает в чем проблема? Почему в данном случае deleteLater у нас не отробатывается?


P.S.
Если кому интересно, вот код который я отправил, что бы баг репорт приняли.
Раскрывающийся текст
#include <QtGui/QApplication>
#include <QtGui/QDialog>
#include <QtGui/QTableWidget>
#include <QtGui/QLineEdit>
#include <QtGui/QVBoxLayout>
#include <QtCore/QTimer>

class MyClass : public QDialog {
    Q_OBJECT

private:
    QLineEdit *le;
    QTableWidget *tw;

public:
    MyClass(QWidget *parent = 0) : QDialog(parent) {
        tw = new QTableWidget(5, 5);
        le = new QLineEdit;
        le->setText("lineEdit");

        QVBoxLayout *layout = new QVBoxLayout;
        layout->addWidget(tw);
        setLayout(layout);

        QTimer *timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), SLOT(test()));
        timer->start(1000);
    }

public slots:
    void test() {
         tw->setCellWidget(0,0,le);
    }
};

#include "debug/main.moc"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MyClass myClass;
    myClass.show();
    return app.exec();
}

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)