Eskil and all,
I found what's causing the freeze: the stylesheet !
If a stylesheet is applied (any stylesheet), when the menu is clicked the
application freezes.
If the stylesheet is commented out, the application works fine.
I attached a small example where it can be reproduced (if it does not compile,
just comment out the problem lines with the missing classes or images, as they
are not important).
If you uncomment the following line in FreezeTestUI.java
//"QDialog { background-color: #EEEEEE; }" +
after right clicking on a row in a table and left click Edit Row menu item, the
freeze will occur !
Thank you,
Danny
----- Original Message ----
From: Danny Sporea <[email protected]>
To: "[email protected]" <[email protected]>
Sent: Wednesday, March 11, 2009 11:05:13 AM
Subject: Re: [Qt-jambi-interest] Upgrading from Jambi 4.3.5 to 4.4.3
I was trying to debug the freezing issue and here is what I found:
The freezing is caused by a custom table widget which has a popup context menu
that emits a signal with the ID of the row that produced the event.
The freeze is happening somewhere in the emit method and it seems to be going
into an infinite loop, as the CPU usage on the javaw process is 99% after the
emit is called.
The event handler method that needs to be executed when the signal is
emited, is never called...
I will come back with more details if I figure something else.
Thanks,
Danny
----- Original Message ----
From: Eskil Abrahamsen Blomfeldt <[email protected]>
To: Danny Sporea <[email protected]>
Cc: "[email protected]" <[email protected]>
Sent: Monday, March 2, 2009 9:08:51 AM
Subject: Re: [Qt-jambi-interest] Upgrading from Jambi 4.3.5 to 4.4.3
Danny Sporea wrote:
> Eskil,
>
> Thanks for your answer, please see below the log for just starting and
> stopping the application, it seems that the correct libraries are loaded...
> I'm using the latest JDK 1.6.0_11 to start the application and following
> flags:
>
[...]
> Loading library: 'QtCore4.dll'...
> - using deployment spec
> - ok!
> Loading library: 'qtjambi.dll'...
> - using deployment spec
> - ok!
> Loading library: 'QtCore4.dll'...
> - already loaded, skipping...
> Loading library: 'com_trolltech_qt_core.dll'...
> - using deployment spec
> - ok!
> Loading library: 'QtGui4.dll'...
> - using deployment spec
> - ok!
> Loading library: 'com_trolltech_qt_gui.dll'...
> - using deployment spec
> - ok!
>
Yes, this seems to be fine. Is this only reproducable with your application, or
are you able to get the same effects in a small example that you could send me?
Right now I wouldn't actually know where to start, since we haven't seen
anything like what you describe.
Also, are the effects reliably reproducable each time (e.g. is it always the
same buttons that are missing text, etc.) or does it seem to have some element
of chaos? Does your application crash at any point? If so, is there a crash
log which I could look at?
-- Eskil
package com.tetratech.cupss;
import com.trolltech.qt.gui.QApplication;
/**
* The Class Cupss.
*/
public final class FreezeTest {
private static FreezeTestUI ui;
public static void main( String[] args ) {
QApplication.initialize( args );
ui = new FreezeTestUI();
ui.show();
QApplication.exec();
}
}
package com.tetratech.cupss;
import com.trolltech.qt.gui.QDialog;
import com.trolltech.qt.gui.QShowEvent;
import com.trolltech.qt.gui.QWidget;
/**
* The Class Welcome.
*/
public class FreezeTestUI extends QDialog {
/** The ui. */
public Ui_FreezeTestUI ui = new Ui_FreezeTestUI();
/**
* Instantiates a new welcome.
*/
public FreezeTestUI(){
ui.setupUi( this );
}
/**
* Instantiates a new welcome.
*
* @param parent the parent
*/
public FreezeTestUI(QWidget parent){
super( parent );
ui.setupUi( this );
}
protected void showEvent( QShowEvent ev ) {
// Define the table columns
String [] columns = { "Test" };
// Define the editable columns
boolean [] editable = { false };
// Initializes the table with the created columns
ui.customTable.initializeTable( this, columns, editable );
// Create new rows
for(int i=0;i<10;i++){
Object [] row = { "Test Row " + i };
// Add rows to the table with their IDs
ui.customTable.addRow( 100+i, row, new String("My Object " + i)
);
}
// Enable add/edit/delete popup menu
ui.customTable.enableMenu( false, true, false );
String style =
//"QDialog { background-color: #EEEEEE; }" +
"";
this.setStyleSheet(style);
}
public void on_customTable_editCustomTableItem( Long id ) {
System.out.println(id);
Object o = ui.customTable.getUserObjectByRowID(id);
System.out.println(">>> " + (String)o);
}
}
<ui version="4.0" language="jambi" >
<class>FreezeTestUI</class>
<widget class="QDialog" name="FreezeTestUI" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>379</width>
<height>242</height>
</rect>
</property>
<property name="windowTitle" >
<string>Dialog</string>
</property>
<widget class="CustomTable" name="customTable" >
<property name="geometry" >
<rect>
<x>10</x>
<y>10</y>
<width>351</width>
<height>221</height>
</rect>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>CustomTable</class>
<extends>QTreeView</extends>
<header>com.tetratech.jambi.widgets</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
package com.tetratech.jambi.widgets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import com.tetratech.cupss.util.Util;
import com.trolltech.qt.core.*;
import com.trolltech.qt.gui.*;
/**
* The Class CustomTable Widget.
*/
public class CustomTable extends QTreeView {
/** The Constant CUSTOM_TABLE_QPALLETTE. */
private static final QPalette CUSTOM_TABLE_QPALETTE = new QPalette();
static {
CUSTOM_TABLE_QPALETTE.setColor( QPalette.ColorGroup.Active,
QPalette.ColorRole.Base, new QColor( 204, 204, 204 ) );
CUSTOM_TABLE_QPALETTE.setColor( QPalette.ColorGroup.Active,
QPalette.ColorRole.AlternateBase, new QColor( 255, 255, 204 ) );
CUSTOM_TABLE_QPALETTE.setColor( QPalette.ColorGroup.Inactive,
QPalette.ColorRole.Base, new QColor( 204, 204, 204 ) );
CUSTOM_TABLE_QPALETTE.setColor( QPalette.ColorGroup.Inactive,
QPalette.ColorRole.AlternateBase, new QColor( 255, 255, 204 ));
CUSTOM_TABLE_QPALETTE.setColor( QPalette.ColorGroup.Disabled,
QPalette.ColorRole.Base, new QColor( 212, 208, 200 ) );
CUSTOM_TABLE_QPALETTE.setColor( QPalette.ColorGroup.Disabled,
QPalette.ColorRole.AlternateBase, new QColor( 255, 255, 204 ) );
}
/** The Constant CONTEXT_MENU_FILE_ADD_IMAGE. */
private static final QPixmap CONTEXT_MENU_FILE_ADD_IMAGE = new QPixmap(
"classpath:img/fileAdd.png" );
/** The Constant CONTEXT_MENU_FILE_EDIT_IMAGE. */
private static final QPixmap CONTEXT_MENU_FILE_EDIT_IMAGE = new
QPixmap( "classpath:img/fileEdit.png" );
/** The Constant CONTEXT_MENU_FILE_DELETE_IMAGE. */
private static final QPixmap CONTEXT_MENU_FILE_DELETE_IMAGE = new
QPixmap( "classpath:img/fileDelete.png" );
/** The add custom table item signal, will always emit -1 as new rowID.
*/
public Signal1<Long> addCustomTableItem = new Signal1<Long>();
/** The edit custom table item signal, will emit the edited row ID. */
public Signal1<Long> editCustomTableItem = new Signal1<Long>();
/** The delete custom table item signal, will emit the deleted row ID. */
public Signal1<Long> deleteCustomTableItem = new Signal1<Long>();
/** The row changed custom table item signal, will emit the changed row ID.
Should be used when on the fly calculations need to be done (e.g. totals). */
public Signal1<Long> rowChanged = new Signal1<Long>();
/** The custom table model that will contain all the table rows. */
private QStandardItemModel customTableModel = null;
/** The editable flags for each of the columns. */
private boolean [] editableColumns = null;
/** The add menu enabled flag. */
private boolean addMenuEnabled = false;
/** The edit menu enabled flag. */
private boolean editMenuEnabled = false;
/** The remove menu enabled flag. */
private boolean removeMenuEnabled = false;
/** The initialized. */
private boolean initialized = false;
/** The popup context menu. */
private QMenu popupMenu = null;
/** The add action. */
private QAction addAction = null;
/** The edit action. */
private QAction editAction = null;
/** The remove action. */
private QAction removeAction = null;
/** The removed rows hash set, will contain the IDs. */
private HashSet removedRows = null;
/** The edited rows hash set, will contain the IDs. */
private HashSet editedRows = null;
/** The added rows hash set, will contain the IDs. */
private HashSet addedRows = null;
/**
* Instantiates a new custom table.
*/
public CustomTable() {
this( null );
}
/**
* Instantiates a new custom table.
*
* @param parent the parent
*/
public CustomTable( QWidget parent ) {
super(parent);
// set default properties
this.setFocusPolicy(com.trolltech.qt.core.Qt.FocusPolicy.WheelFocus);
this.setFrameShape(com.trolltech.qt.gui.QFrame.Shape.Box);
this.setFrameShadow(com.trolltech.qt.gui.QFrame.Shadow.Plain);
this.setVerticalScrollBarPolicy(com.trolltech.qt.core.Qt.ScrollBarPolicy.ScrollBarAsNeeded);
this.setHorizontalScrollBarPolicy(com.trolltech.qt.core.Qt.ScrollBarPolicy.ScrollBarAlwaysOff);
this.setProperty("showDropIndicator", false);
this.setRootIsDecorated(false);
this.setUniformRowHeights(false);
this.setItemsExpandable(false);
this.setSortingEnabled(true);
this.setAnimated(true);
this.setAlternatingRowColors(true);
// make header interactive and not movable
this.header().setResizeMode(QHeaderView.ResizeMode.Interactive);
this.header().setMovable(false);
// sets up the palette
this.setPalette( CUSTOM_TABLE_QPALETTE );
}
/**
* Initializes the table with a parent and all columns.
*
* @param parent the parent
* @param columns the header columns as string array
* @param editable the editable columns as boolean array
*/
public void initializeTable( QObject parent, String [] columns, boolean []
editable ) {
// attribute initializations
initialized = false;
popupMenu = null;
addMenuEnabled = false;
editMenuEnabled = false;
removeMenuEnabled = false;
addedRows = new HashSet();
editedRows = new HashSet();
removedRows = new HashSet();
editableColumns = new boolean[ columns.length + 1 ];
customTableModel = new QStandardItemModel( 0, columns.length + 1,
parent ) {
@Override
public Object data(QModelIndex index, int role) {
Object data = super.data(index, role);
if (role == Qt.ItemDataRole.DisplayRole && data
instanceof Double)
data = String.format("%1$.2f", data);
return data;
}
};
// initialize header data
customTableModel.setHeaderData(0, Qt.Orientation.Horizontal, tr("ID"));
editableColumns[ 0 ] = false;
for ( int i = 1; i <= columns.length; i++ ) {
customTableModel.setHeaderData(i, Qt.Orientation.Horizontal,
tr(columns[ i - 1 ]));
editableColumns[ i ] = editable[ i - 1 ];
}
// connect to slot for item changes events
customTableModel.itemChanged.connect( this, "myItemChanged(
QStandardItem )" );
this.setModel(customTableModel);
// hides column 0 that contains the ID
this.setColumnHidden(0, true);
// sets the column sizes
int size = ( int ) ( this.width() - ( columns.length * 3 ) ) /
columns.length;
this.header().setDefaultSectionSize(size);
for ( int i = 0; i < this.header().count(); i++ ) {
this.header().resizeSection( i, size );
}
initialized = true;
}
/**
* Enables the context popup menu.
*
* @param add the add menu enabled flag
* @param edit the edit menu enabled flag
* @param remove the remove menu enabled flag
*/
public void enableMenu( boolean add, boolean edit, boolean remove ) {
// if any menu item enabled
if ( add || edit || remove ) {
this.addMenuEnabled = add;
this.editMenuEnabled = edit;
this.removeMenuEnabled = remove;
// create popup menu
popupMenu = new QMenu(this);
// add action
if ( add ) {
addAction = new QAction( tr("Add Row"), this );
addAction.setStatusTip( tr("Adds a row to the table.")
);
addAction.setIcon( CONTEXT_MENU_FILE_ADD_IMAGE );
addAction.triggered.connect(this, "myAddItem()");
popupMenu.addAction(addAction);
}
// edit action
if ( edit ) {
editAction = new QAction( tr("Edit Row"), this );
editAction.setStatusTip( tr("Edits the selected row in
the table.") );
editAction.setIcon( CONTEXT_MENU_FILE_EDIT_IMAGE );
editAction.triggered.connect(this, "myEditItem()");
popupMenu.addAction(editAction);
}
// delete action
if ( remove ) {
removeAction = new QAction( tr("Remove Row"), this );
removeAction.setStatusTip( tr("Removes the row from the
table.") );
removeAction.setIcon( CONTEXT_MENU_FILE_DELETE_IMAGE );
removeAction.triggered.connect(this, "myDeleteItem()");
popupMenu.addAction(removeAction);
}
}
}
/* (non-Javadoc)
* @see
com.trolltech.qt.gui.QAbstractScrollArea#contextMenuEvent(com.trolltech.qt.gui.QContextMenuEvent)
*/
public void contextMenuEvent( QContextMenuEvent event ) {
if ( popupMenu != null ) {
if ( editMenuEnabled ) {
if ( ( this.selectedIndexes() == null ) || (
this.selectedIndexes().isEmpty() ) )
editAction.setEnabled( false );
else
editAction.setEnabled( true );
}
if ( removeMenuEnabled ) {
if ( ( this.selectedIndexes() == null ) || (
this.selectedIndexes().isEmpty() ) )
removeAction.setEnabled( false );
else
removeAction.setEnabled( true );
}
popupMenu.exec(event.globalPos());
}
}
/**
* Adds a row to the table as an existing row with ID and objects and user
object,
* if specified.
*
* @param rowID the row id
* @param row the row as object array
* @param userObject the user object
*/
public void addRow( long rowID, Object [] row, Object userObject ) {
if ( initialized ) {
// disconnect the item changed signal in order to not have the
added items as changed
customTableModel.itemChanged.disconnect( this, "myItemChanged(
QStandardItem )" );
// adds new row to the model
int idx = customTableModel.rowCount();
customTableModel.insertRow( idx );
customTableModel.setData( customTableModel.index( idx, 0 ), new
Long( rowID ) );
if ( userObject != null )
customTableModel.setData( customTableModel.index( idx,
0 ), userObject, Qt.ItemDataRole.UserRole );
customTableModel.item( idx, 0 ).setEditable( editableColumns[ 0
] );
for ( int i = 1; i <= row.length; i++ ) {
customTableModel.setData( customTableModel.index( idx,
i ), row[ i - 1 ] );
customTableModel.item( idx, i ).setEditable(
editableColumns[ i ] );
customTableModel.item( idx, i ).setToolTip(
customTableModel.item( idx, i ).text() );
if ( row[ i - 1 ] instanceof Double ) {
customTableModel.item( idx, i
).setTextAlignment( Qt.AlignmentFlag.AlignRight );
}
else {
customTableModel.item( idx, i
).setTextAlignment( Qt.AlignmentFlag.AlignLeft );
}
}
// reconnect the item changed signal
customTableModel.itemChanged.connect( this, "myItemChanged(
QStandardItem )" );
}
}
/**
* Adds a row to the table as an existing row with ID and objects.
*
* @param rowID the row id
* @param row the row as object array
*/
public void addRow( long rowID, Object [] row ) {
addRow( rowID, row, null );
}
/**
* Adds a row to the table as a new row objects.
*
* @param row the row as object array
*/
public void addNewRow( Object [] row ) {
if ( initialized ) {
// gets a new negative unique id and adds a new row
Long id = getNextNegativeID();
addRow( id.longValue(), row );
// saves the new row id in the added rows set
addedRows.add( id );
}
}
/**
* Adds a row to the table as a new row objects and an user object.
*
* @param row the row as object array
* @param userObject the user object
*/
public void addNewRow( Object [] row, Object userObject ) {
if ( initialized ) {
// gets a new negative unique id and adds a new row
Long id = getNextNegativeID();
addRow( id.longValue(), row, userObject );
// saves the new row id in the added rows set
addedRows.add( id );
}
}
/**
* Updates a row in the table based on the row id by updating the objects
and
* the user object, if specified.
*
* @param rowID the row id
* @param row the row as object array
* @param userObject the user object
*/
public void updateRow( long rowID, Object [] row, Object userObject ) {
if ( initialized ) {
// edits the row in the model
int len = customTableModel.rowCount();
for ( int idx = 0; idx < len; idx++ ) {
long id = ( ( Long ) customTableModel.data( idx, 0,
Qt.ItemDataRole.EditRole ) ).longValue();
if ( id == rowID ) {
if ( userObject != null )
customTableModel.setData(
customTableModel.index( idx, 0 ), userObject, Qt.ItemDataRole.UserRole );
for ( int i = 1; i <= row.length; i++ ) {
customTableModel.setData(
customTableModel.index( idx, i ), row[ i - 1 ] );
customTableModel.item( idx, i ).setToolTip(
customTableModel.item( idx, i ).text() );
if ( row[ i - 1 ] instanceof Double ) {
customTableModel.item( idx, i
).setTextAlignment( Qt.AlignmentFlag.AlignRight );
}
else {
customTableModel.item( idx, i
).setTextAlignment( Qt.AlignmentFlag.AlignLeft );
}
}
}
}
}
}
/**
* Updates a row in the table based on the row id by updating the objects.
*
* @param rowID the row id
* @param row the row as object array
*/
public void updateRow( long rowID, Object [] row ) {
updateRow( rowID, row, null );
}
/**
* Gets the newly added rows to the table as an array list of Object [].
* The first object in the Object array will be a Long with the ID.
* The last object in the Object array will be the user object,
* if specified.
*
* @return the added rows as an array list of Object []
*/
public ArrayList getAddedRows() {
ArrayList lst = null;
if ( initialized ) {
lst = new ArrayList( addedRows.size() );
for ( Iterator it = addedRows.iterator(); it.hasNext(); ) {
long id = ( ( Long ) it.next() ).longValue();
lst.add( getRowByID( id ) );
}
}
return lst;
}
/**
* Gets the edited rows of the table as an array list of Object [].
* The first object in the Object array will be a Long with the ID.
* The last object in the Object array will be the user object,
* if specified.
*
* @return the edited rows as an array list of Object []
*/
public ArrayList getEditedRows() {
ArrayList lst = null;
if ( initialized ) {
lst = new ArrayList( editedRows.size() );
for ( Iterator it = editedRows.iterator(); it.hasNext(); ) {
long id = ( ( Long ) it.next() ).longValue();
lst.add( getRowByID( id ) );
}
}
return lst;
}
/**
* Gets the removed row IDs as an ArrayList of Long objects.
*
* @return the removed row IDs as an ArrayList of Long objects
*/
public ArrayList getRemovedRowIDs() {
return new ArrayList( removedRows );
}
/**
* Gets all the rows of the table as an array list of Object [].
* The first object in the Object array will be a Long with the ID.
* The last object in the Object array will be the user object,
* if specified.
*
* @return the edited rows as an array list of Object []
*/
public ArrayList getAllRows() {
ArrayList lst = null;
if ( initialized ) {
int len = customTableModel.rowCount();
lst = new ArrayList( len );
for ( int idx = 0; idx < len; idx++ ) {
long id = ( ( Long ) customTableModel.data( idx, 0,
Qt.ItemDataRole.EditRole ) ).longValue();
lst.add( getRowByID( id ) );
}
}
return lst;
}
/**
* Gets the user object by row id.
*
* @param rowID the row id
*
* @return the user object by row id
*/
public Object getUserObjectByRowID( long rowID ) {
int len = customTableModel.rowCount();
for ( int idx = 0; idx < len; idx++ ) {
long id = ( ( Long ) customTableModel.data( idx, 0,
Qt.ItemDataRole.EditRole ) ).longValue();
if ( id == rowID ) {
return customTableModel.data( customTableModel.index(
idx, 0 ), Qt.ItemDataRole.UserRole );
}
}
return null;
}
/**
* My add item, emits the add custom table item signal with the added ID as
-1.
*/
public void myAddItem() {
addCustomTableItem.emit( new Long( -1 ) );
}
/**
* My edit item, emits the edit custom table item signal with the edited ID.
*/
public void myEditItem() {
editCustomTableItem.emit( getID( this.selectedIndexes().get( 0 ) ) );
}
/**
* My delete item, emits the delete custom table item signal with the
deleted ID.
*/
public void myDeleteItem() {
QMessageBox.StandardButton ret;
QMessageBox msgBox = new QMessageBox( QMessageBox.Icon.Question,
"CUPSS", "Are you sure you want to delete this row ?" );
msgBox.setStyleSheet( Util.getBaseStyle() );
msgBox.setStandardButtons( new QMessageBox.StandardButtons(
QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No ) );
msgBox.setDefaultButton( QMessageBox.StandardButton.No );
ret = QMessageBox.StandardButton.resolve( msgBox.exec() );
msgBox.dispose();
if ( ret.equals( QMessageBox.StandardButton.Yes ) ) {
Long id = getID( this.selectedIndexes().get( 0 ) );
if ( id.longValue() < 0 ) addedRows.remove( id );
else {
editedRows.remove( id );
removedRows.add( id );
}
customTableModel.removeRow( this.selectedIndexes().get( 0
).row() );
deleteCustomTableItem.emit( id );
}
}
/**
* My item changed slot to treat item changes by adding them to the edited
rows hash set.
*
* @param item the item that changed
*/
public void myItemChanged( QStandardItem item ) {
Long id = getID( item.row() );
if ( id.longValue() > 0 ) editedRows.add( id );
rowChanged.emit( id );
}
/**
* Gets the row by id as an Object [].
*
* @param id the id
*
* @return the row by id as an Object []
*/
private Object [] getRowByID( long id ) {
int len = editableColumns.length;
Object [] row = new Object[ len + 1 ];
int rowId = -1;
for ( int i = 0; i < customTableModel.rowCount(); i++ ) {
Long lngId = ( Long ) customTableModel.data( i, 0,
Qt.ItemDataRole.EditRole );
if ( lngId.longValue() == id ) {
rowId = i;
}
}
for ( int i = 0; i < customTableModel.columnCount(); i++ ) {
row[ i ] = customTableModel.data( rowId, i,
Qt.ItemDataRole.EditRole );
}
row[ len ] = customTableModel.data( rowId, 0, Qt.ItemDataRole.UserRole
);
return row;
}
/**
* Gets the row ID by row number.
*
* @param row the row number
*
* @return the row ID
*/
private Long getID( int row ) {
return ( Long ) customTableModel.data( row, 0,
Qt.ItemDataRole.EditRole );
}
/**
* Gets the row ID of a model index.
*
* @param idx the model index
*
* @return the row ID
*/
private Long getID( QModelIndex idx ) {
return ( Long ) customTableModel.data( idx.row(), 0,
Qt.ItemDataRole.EditRole );
}
/**
* Gets the next negative ID for newly added rows.
*
* @return the next negative id
*/
private Long getNextNegativeID() {
int len = customTableModel.rowCount();
long min = 0;
for ( int i = 0; i < len; i++ ) {
long id = ( ( Long ) customTableModel.data( i, 0,
Qt.ItemDataRole.EditRole ) ).longValue();
if ( id < min ) min = id;
}
min -= 1;
return new Long( min );
}
/**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args) {
QApplication.initialize(args);
CustomTable customTable = new CustomTable();
customTable.show();
QApplication.exec();
}
}
_______________________________________________
Qt-jambi-interest mailing list
[email protected]
http://lists.trolltech.com/mailman/listinfo/qt-jambi-interest