[Python-modules-team] Bug#599612: python-qt4: removeItemWidget() wrongly removes QTreeWidgetItem itself

Leonardo Vainsencher elweins at gmail.com
Sat Oct 9 15:01:40 UTC 2010


Package: python-qt4
Version: 4.7.3-1+b1
Severity: normal

In a Python program I use setItemWidget() to add widgets to columns
of QTreeWidgetItems (eg QPushButton, QComboBox, etc).

When a widget is no longer needed it is removed using removeItemWidget(),
however this action causes the removal of the QTreeWidgetItem itself
instead of just removing the item widget attached by setItemWidget().

Problem goes away if extra references to QTreeWidgetItems are kept
somewhere outside the QTreeWidget instance.

This problem does not occur in a C++ implementation.

Files attached can help demonstrate the issue:
bug.py:
    Python script demonstrating the bug; if run without arguments
    bug shows up (disappearing QTreeWidgetItems); if run with a single
    argument 'workaround' the script artificially keeps extra references
    causing QTreeWidgetItems to stay in their places; program uses a
    timer to add and remove item widgets after short delays;
    ctl-Q quits the program.
bug.ui:
    GUI file from designer-qt4 for both python version and C++ version.
bug.cpp bug.h:
    C++ "translation" of bug.py; shows setItemWidget/removeItemWidget
    working as expected.
bug.mk
    Simple Makefile to compile everything.

*** bug.py
#! /usr/bin/python

from PyQt4 import QtGui as Qg
from PyQt4 import QtCore as Qc
from PyQt4 import Qt
from ui_bug import Ui_MainWindow
import sys

class MainWindow(Qg.QMainWindow, Ui_MainWindow):
    def __init__(self, workaround=True):
	Qg.QMainWindow.__init__(self)
	Ui_MainWindow.__init__(self)
	self.setupUi(self)
	self.connect(self.actionExit, Qc.SIGNAL('triggered()'), self.__exit)
	self.__timer = Qc.QTimer(self)
	self.connect(self.__timer, Qc.SIGNAL('timeout()'), self.__sequence)
	self.__state = 0
	self.__timer.setSingleShot(True)
	self.__timer.start(2000)
	if workaround:
	    print 'WARNING: using removeItemWidget() bug workaround'
	    tw = self.treeWidget
	    nitems = tw.topLevelItemCount()
	    self.__items = [tw.topLevelItem(k) for k in range(nitems)]
	else: self.__items=[]

    def __sequence(self):
	treew = self.treeWidget
	nitems = treew.topLevelItemCount()
	if nitems < 1:
	    print 'ERROR: no items left'; return
	col = 1
	if self.__state==0:
	    print 'adding %d item widgets to column %d' % (nitems,col)
	    for k in range(nitems):
		item = treew.topLevelItem(k)
		pbut = Qg.QPushButton(item.text(col),self)
		treew.setItemWidget(item, col, pbut)
		item.setText(col, '')
	    self.__state+=1
	    self.__timer.start(2000)
	elif self.__state==1:
	    print 'removing %d item widgets from column %d' % (nitems,col)
	    for k in range(nitems):
		item = treew.topLevelItem(k)
		pbut = treew.itemWidget(item, col)
		item.setText(col, pbut.text())
		treew.removeItemWidget(item, col)
	    self.__state+=1
	    self.__timer.start(2000)

    def __exit(self):
	Qg.QApplication.closeAllWindows()

if __name__ == '__main__':
    app = Qg.QApplication([])
    gui = MainWindow('workaround' in sys.argv[1:])
    gui.show()
    sys.exit(app.exec_())

*** bug.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QTreeWidget" name="treeWidget">
      <property name="rootIsDecorated">
       <bool>false</bool>
      </property>
      <column>
       <property name="text">
        <string>1</string>
       </property>
      </column>
      <column>
       <property name="text">
        <string>2</string>
       </property>
      </column>
      <column>
       <property name="text">
        <string>3</string>
       </property>
      </column>
      <item>
       <property name="text">
        <string>a</string>
       </property>
       <property name="text">
        <string>b</string>
       </property>
       <property name="text">
        <string>c</string>
       </property>
      </item>
      <item>
       <property name="text">
        <string>x</string>
       </property>
       <property name="text">
        <string>y</string>
       </property>
       <property name="text">
        <string>z</string>
       </property>
      </item>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>26</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuFile">
    <property name="title">
     <string>File</string>
    </property>
    <addaction name="actionExit"/>
   </widget>
   <addaction name="menuFile"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <action name="actionExit">
   <property name="text">
    <string>Exit</string>
   </property>
   <property name="shortcut">
    <string>Ctrl+Q</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>

*** bug.cpp
#include "bug.h"
#include "ui_bug.h"
#include <QtCore/QTimer>
#include <QtGui/QApplication>
#include <QtGui/QPushButton>
#include <iostream>
#include <boost/format.hpp>

using namespace std;
using boost::format;

MainWindow::MainWindow(void)
{
    _state = 0;
    _timer = new QTimer(this);
    _priv = new Ui_MainWindow;
    _priv->setupUi(this);
    connect(_priv->actionExit, SIGNAL(triggered()), SLOT(_exit()));
    connect(_timer, SIGNAL(timeout()), SLOT(_sequence()));
    _timer->setSingleShot(true);
    _timer->start(2000);
}

void MainWindow::_exit(void)
{
    QApplication::closeAllWindows();
}

void MainWindow::_sequence(void)
{
    QTreeWidget* treew= _priv->treeWidget;
    const int nitems= treew->topLevelItemCount();
    if (nitems < 1) {
	cout << "ERROR: no items left" << endl;
	return;
    }
    const int col=1;
    if (_state==0) {
	cout << format("adding %d item widgets to column %d\n") % nitems % col;
	for (int k=0; k < nitems; k++) {
	    QTreeWidgetItem* item= treew->topLevelItem(k);
	    QPushButton* pbut= new QPushButton(item->text(col), this);
	    treew->setItemWidget(item, col, pbut);
	    item->setText(col, "");
	}
	_state+=1;
	_timer->start(2000);
    } else if (_state==1) {
	cout << format("removing %d item widgets from column %d\n") % nitems % col;
	for (int k=0; k < nitems; k++) {
	    QTreeWidgetItem* item= treew->topLevelItem(k);
	    QPushButton* itemw=
		dynamic_cast<QPushButton*>(treew->itemWidget(item, col));
	    item->setText(col, itemw->text());
	    treew->removeItemWidget(item, col);
	    delete itemw;
	}
	_state+=1;
	_timer->start(2000);
    }
}

int main(int ac, char** av)
{
    QApplication app(ac,av);
    MainWindow gui;
    gui.show();
    return app.exec();
}

*** bug.h
#ifndef BUG_H
#define BUG_H

#include <QtGui/QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(void);
private slots:
    void _exit(void);
    void _sequence(void);
private:
    class Ui_MainWindow* _priv;
    class QTimer* _timer;
    int _state;
};
#endif

*** bug.mk
uiparts	= bug.ui

uihdrs	= bug.h
sources	= bug.cpp
target	= bug.exe

pyuis	= $(uiparts:%.ui=ui_%.py)
ccuis	= $(uiparts:%.ui=ui_%.h)
mocs	= $(uihdrs:%.h=moc_%.cpp)

qtinc  = /usr/include/qt4
qtlib	= -L/usr/lib/qt4/lib

INCL    = -I$(qtinc)/QtCore -I$(qtinc)/QtGui -I$(qtinc)
LIBS	= $(qtlib) -lQtGui -lQtCore

.IGNORE:	all

all:	$(pyuis) $(target)

$(target):	$(mocs) $(sources) $(uihdrs) $(ccuis)
	g++ -g -o $@ $(INCL) $(mocs) $(sources) $(LIBS)

ui_%.py:	%.ui
	pyuic4 -x -o $@ $<

ui_%.h:		%.ui
	uic-qt4 -o $@ $<

moc_%.cpp:	%.h
	moc-qt4 -o $@ $(INCL) $<


-- System Information:
Debian Release: squeeze/sid
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: i386 (i686)

Kernel: Linux 2.6.32-5-686 (SMP w/4 CPU cores)
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)
Shell: /bin/sh linked to /bin/bash

Versions of packages python-qt4 depends on:
ii  libc6                       2.11.2-6     Embedded GNU C Library: Shared lib
ii  libgcc1                     1:4.4.4-8    GCC support library
ii  libpython2.6                2.6.6-3      Shared Python runtime library (ver
ii  libqt4-assistant            4:4.6.3-1+b1 Qt 4 assistant module
ii  libqt4-designer             4:4.6.3-1+b1 Qt 4 designer module
ii  libqt4-help                 4:4.6.3-1+b1 Qt 4 help module
ii  libqt4-network              4:4.6.3-1+b1 Qt 4 network module
ii  libqt4-script               4:4.6.3-1+b1 Qt 4 script module
ii  libqt4-scripttools          4:4.6.3-1+b1 Qt 4 script tools module
ii  libqt4-svg                  4:4.6.3-1+b1 Qt 4 SVG module
ii  libqt4-test                 4:4.6.3-1+b1 Qt 4 test module
ii  libqt4-webkit               4:4.6.3-1+b1 Qt 4 WebKit module
ii  libqt4-xml                  4:4.6.3-1+b1 Qt 4 XML module
ii  libqt4-xmlpatterns          4:4.6.3-1+b1 Qt 4 XML patterns module
ii  libqtcore4                  4:4.6.3-1+b1 Qt 4 core module
ii  libqtgui4                   4:4.6.3-1+b1 Qt 4 GUI module
ii  libstdc++6                  4.4.4-8      The GNU Standard C++ Library v3
ii  python                      2.6.5-13     interactive high-level object-orie
ii  python-sip [sip-api-7.1]    4.10.2-1     Python/C++ bindings generator runt
ii  python-support              1.0.10       automated rebuilding support for P

python-qt4 recommends no packages.

Versions of packages python-qt4 suggests:
pn  python-qt4-dbg                <none>     (no description available)

-- no debconf information





More information about the Python-modules-team mailing list