Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace cell widgets by pixmaps while dragging on overlays #18

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
109 changes: 95 additions & 14 deletions dat/gui/cellcontainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ def __init__(self, cellInfo=None, widget=None, error=None, parent=None):

self._parameter_hovered = None
self._insert_pos = None
self._dragging = False
self._fake_widget = None

# Notifications
app = get_vistrails_application()
Expand All @@ -62,7 +64,7 @@ def __init__(self, cellInfo=None, widget=None, error=None, parent=None):

# Overlay
self._overlay = None
self._overlay_scrollarea = QtGui.QScrollArea(self)
self._overlay_scrollarea = QtGui.QScrollArea() # FIXME: no parent
self._overlay_scrollarea.setObjectName('overlay_scrollarea')
self._overlay_scrollarea.setStyleSheet(
'QScrollArea#overlay_scrollarea {'
Expand All @@ -72,6 +74,7 @@ def __init__(self, cellInfo=None, widget=None, error=None, parent=None):
' background-color: transparent;'
'}')
self._overlay_scrollarea.setWidgetResizable(True)
self._overlay_scrollarea.setVisible(False)

# Toolbar
self._container_toolbar = QtGui.QToolBar(self)
Expand Down Expand Up @@ -144,17 +147,67 @@ def setCellInfo(self, cellInfo):
app.unregister_notification(
'dragging_to_overlays', self._set_dragging)

def _set_dragging(self, dragging):
"""This is a hack to avoid an issue with Qt's mouse event propagation.
def _make_fake_widget(self):
if self._fake_widget is None:
if hasattr(self.containedWidget, 'grabWindowPixmap'):
pixmap = self.containedWidget.grabWindowPixmap()
else:
pixmap = QtGui.QPixmap.grabWidget(self.containedWidget)

self.containedWidget.setAttribute(
QtCore.Qt.WA_TransparentForMouseEvents, True)
self.containedWidget.setParent(None)
self.containedWidget.hide()

If we don't set TransparentForMouseEvents on the overlay, when the drag
enters, the overlay will receive the mouse event and propagate it to
us. Thus it is on the call stack and we can't replace it with another
overlay... It would cause a segmentation fault on Mac OS.
self._fake_widget = QtGui.QLabel(self)
self._fake_widget.setPixmap(pixmap)
self._fake_widget.setAttribute(
QtCore.Qt.WA_TransparentForMouseEvents, True)

def _set_dragging(self, dragging):
"""This is a hack to workaround issues related to dragging.
"""
self._dragging = dragging

# Issue with Qt's mouse event propagation.
#
# If we don't set TransparentForMouseEvents on the overlay, when the
# drag enters, the overlay will receive the mouse event and propagate
# it to us. Thus it is on the call stack and we can't replace it with
# another overlay... It would cause a segmentation fault on Mac OS.
self._overlay_scrollarea.setAttribute(
QtCore.Qt.WA_TransparentForMouseEvents, dragging)

# Issue with some non-Qt widgets, such as VTK's (that use direct
# rendering)
# We can just replace the cell with an image of the previous content,
# like the spreadsheet does with QCellPresenter
if dragging:
if self.containedWidget is not None:
self._make_fake_widget()
self._fake_widget.raise_()
self._fake_widget.show()

self._overlay_scrollarea.setParent(self)
self._overlay_scrollarea.setVisible(True)
self._overlay_scrollarea.lower()

self.do_layout()
else:
if self._fake_widget is not None:
self._fake_widget.setParent(None)
self._fake_widget.deleteLater()
self._fake_widget = None
if self.containedWidget is not None:
self.containedWidget.setAttribute(
QtCore.Qt.WA_TransparentForMouseEvents, False)

self.containedWidget.setParent(self)
self.containedWidget.show()
self.containedWidget.raise_()

self.do_layout()

def _variable_added(self, controller, varname, renamed_from=None):
if (renamed_from is None or
controller != self._controller or
Expand Down Expand Up @@ -208,24 +261,24 @@ def setWidget(self, widget):
This is called by the spreadsheet to put or remove a visualization in
this cell.
"""
if widget != self.containedWidget:
assert self._fake_widget is None
if widget is not self.containedWidget:
if self.containedWidget:
self.containedWidget.setParent(None)
self.containedWidget.deleteLater()
self.toolBar = None
if widget:
widget.setParent(self)
widget.show()
self.containedWidget = widget

if widget is None:
return
widget.raise_()
self._set_toolbar_buttons(True)

self.contentsUpdated()

def takeWidget(self):
assert self._fake_widget is None
widget = self.containedWidget
if widget is not None:
widget.setParent(None)
Expand Down Expand Up @@ -273,7 +326,7 @@ def contentsUpdated(self):
self._set_overlay(None)

def _set_overlay(self, overlay_class, **kwargs):
if overlay_class is None:
if overlay_class is None and not self._execute_pending:
# Default overlay
if self._plot is not None and self.has_error():
self._set_overlay(VariableDroppingOverlay, overlayed=False)
Expand All @@ -293,22 +346,45 @@ def _set_overlay(self, overlay_class, **kwargs):

if overlay_class is None:
self._overlay = None
self._overlay_scrollarea.lower()
if not self._dragging:
self._overlay_scrollarea.setParent(None)
self._overlay_scrollarea.setVisible(False)
if self._plot is not None:
self._set_toolbar_buttons(True)
else:
self._set_toolbar_buttons(None)

if self._fake_widget is not None:
self._fake_widget.setParent(None)
self._fake_widget.deleteLater()
self._fake_widget = None
if self.containedWidget:
self.containedWidget.setParent(self)
self.containedWidget.show()
self.containedWidget.raise_()
self.containedWidget.setAttribute(
QtCore.Qt.WA_TransparentForMouseEvents, False)
self.do_layout()

# Now that we are done with the overlay, we can go on with a
# deferred execution
if self._execute_pending:
self.update_pipeline()
self._execute_pending = False
else:
if self.containedWidget is not None:
self._make_fake_widget()
self._fake_widget.lower()
self._fake_widget.show()

self._overlay = overlay_class(self, **kwargs)
if not self._dragging:
self._overlay_scrollarea.setParent(self)
self._overlay_scrollarea.setVisible(True)
self._overlay_scrollarea.setWidget(self._overlay)
self._overlay.show()
self._overlay_scrollarea.raise_()

self.do_layout()
self._set_toolbar_buttons(None)

Expand Down Expand Up @@ -347,8 +423,13 @@ def resizeEvent(self, event):
self.do_layout()

def do_layout(self):
if self.containedWidget is not None:
self.containedWidget.setGeometry(
if self._fake_widget is not None:
widget = self._fake_widget
else:
widget = self.containedWidget

if widget is not None:
widget.setGeometry(
4, 4,
self.width() - 8, self.height() - 8)
self._overlay_scrollarea.setGeometry(
Expand Down