00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 from python_qt_binding.QtCore import qDebug, QEvent, QPoint, QRect, Qt
00032 from python_qt_binding.QtGui import QApplication, QDockWidget, QMouseEvent
00033
00034 from .dockable_main_window import DockableMainWindow
00035 from .reparent_event import ReparentEvent
00036
00037
00038 class DockWidget(QDockWidget):
00039
00040 """Dock widget with the capability to be reparented via drag-and-drop to any other main window."""
00041
00042 def __init__(self, container_manager):
00043 super(DockWidget, self).__init__()
00044 self._container_manager = container_manager
00045 if self._container_manager is not None:
00046 self.event = self._event
00047 self._dragging_parent = None
00048 self._dragging_local_pos = None
00049 self._releasing_and_repressing_while_dragging = False
00050 self._main_windows = []
00051
00052 def _event(self, e):
00053 if e.type() == QEvent.MouseButtonPress and e.button() == Qt.LeftButton:
00054 qDebug('%spress, rel=%s, global=%s, diff=%s' % ((' - pseudo ' if self._releasing_and_repressing_while_dragging else ''), e.pos(), e.globalPos(), e.globalPos() - self.pos()))
00055 if e.type() == QEvent.MouseButtonRelease and e.button() == Qt.LeftButton:
00056 qDebug('%srelease, rel=%s, global=%s, diff=%s' % ((' - pseudo ' if self._releasing_and_repressing_while_dragging else ''), e.pos(), e.globalPos(), e.globalPos() - self.pos()))
00057
00058
00059
00060 if self._dragging_parent is None and e.type() == QEvent.MouseButtonPress and e.button() == Qt.LeftButton and bool(self.features() & QDockWidget.DockWidgetMovable):
00061 self._dragging_local_pos = e.pos()
00062
00063 if self._dragging_parent is None and self._dragging_local_pos is not None and e.type() == QEvent.Move and QApplication.mouseButtons() & Qt.LeftButton:
00064 if self._widget_at(e.pos()) is not None:
00065 qDebug('DockWidget._event() start drag, dockwidget=%s, parent=%s, floating=%s, pos=%s' % (str(self), str(self.parent()), str(self.isFloating()), str(self._dragging_local_pos)))
00066 self._dragging_parent = self.parent()
00067
00068 self.setAttribute(Qt.WA_TransparentForMouseEvents)
00069
00070
00071 self._main_windows = [self._container_manager.get_root_main_window()]
00072 for container in self._container_manager.get_containers():
00073 if container == self:
00074 continue
00075 self._main_windows.append(container.main_window)
00076
00077
00078 if self._dragging_local_pos is not None and e.type() == QEvent.MouseButtonRelease and e.button() == Qt.LeftButton and not self._releasing_and_repressing_while_dragging:
00079 self._dragging_local_pos = None
00080
00081 if self._dragging_parent is not None and e.type() == QEvent.MouseButtonRelease and e.button() == Qt.LeftButton and not self._releasing_and_repressing_while_dragging:
00082 qDebug('DockWidget._event() stop drag, dockwidget=%s, parent=%s\n' % (self, self.parent()))
00083 self._dragging_parent = None
00084 self.setAttribute(Qt.WA_TransparentForMouseEvents, False)
00085 self._main_windows = []
00086
00087 if self._dragging_parent is not None and e.type() == QEvent.MouseMove and e.buttons() & Qt.LeftButton and not self._releasing_and_repressing_while_dragging:
00088 widget = self._widget_at(e.globalPos())
00089 new_parent = self._get_new_parent(widget)
00090
00091 if new_parent is not None and new_parent != self.parent():
00092 self._releasing_and_repressing_while_dragging = True
00093
00094
00095 mouse_release_event = QMouseEvent(QEvent.MouseButtonRelease, self._dragging_local_pos, e.globalPos(), Qt.LeftButton, Qt.NoButton, e.modifiers())
00096 QApplication.instance().postEvent(self, mouse_release_event)
00097 QApplication.sendPostedEvents()
00098
00099
00100 reparent_event = ReparentEvent(self, new_parent)
00101 QApplication.instance().postEvent(self._container_manager, reparent_event)
00102 QApplication.sendPostedEvents()
00103
00104
00105 self.setAttribute(Qt.WA_TransparentForMouseEvents, False)
00106
00107
00108 mouse_repress_event = QMouseEvent(QEvent.MouseButtonPress, self._dragging_local_pos, e.globalPos(), Qt.LeftButton, Qt.LeftButton, e.modifiers())
00109 QApplication.instance().postEvent(self, mouse_repress_event)
00110 QApplication.sendPostedEvents()
00111
00112
00113
00114 mouse_move_event = QMouseEvent(QEvent.MouseMove, self._dragging_local_pos, e.globalPos() + QPoint(QApplication.startDragDistance(), 1), Qt.NoButton, Qt.LeftButton, e.modifiers())
00115 QApplication.instance().postEvent(self, mouse_move_event)
00116 QApplication.sendPostedEvents()
00117
00118 mouse_move_event = QMouseEvent(QEvent.MouseMove, self._dragging_local_pos, e.globalPos(), Qt.NoButton, Qt.LeftButton, e.modifiers())
00119 QApplication.instance().postEvent(self, mouse_move_event)
00120 QApplication.sendPostedEvents()
00121
00122
00123 self.setAttribute(Qt.WA_TransparentForMouseEvents)
00124
00125 self._releasing_and_repressing_while_dragging = False
00126
00127 return super(DockWidget, self).event(e)
00128
00129 def _get_new_parent(self, widget):
00130 from .dock_widget_container import DockWidgetContainer
00131 if isinstance(widget, DockWidgetContainer):
00132 if widget.isFloating():
00133 return None
00134 widget = widget.parent()
00135 while widget is not None:
00136 if isinstance(widget, DockableMainWindow):
00137 break
00138 if not callable(widget.parent):
00139 return None
00140 widget = widget.parent()
00141 return widget
00142
00143 def _widget_at(self, global_point):
00144
00145 widget = QApplication.widgetAt(global_point)
00146
00147 root_main_window = self._container_manager.get_root_main_window()
00148
00149
00150 if widget == root_main_window and not self._widget_contains(root_main_window, global_point):
00151
00152 widget = None
00153
00154
00155 if widget is None and self.isFloating():
00156
00157 overlapping = {}
00158 for main_window in self._main_windows:
00159 if self._widget_contains(main_window, global_point):
00160 parent = main_window.parent()
00161 is_floating = parent is None or parent.isFloating()
00162 overlapping[main_window] = is_floating
00163
00164
00165 if len(overlapping) == 1:
00166
00167 widget, _ = overlapping.popitem()
00168 elif len(overlapping) > 1:
00169
00170
00171 overlapping_docked = [mw for mw, floating in overlapping.iteritems() if not floating]
00172
00173
00174
00175 if len(overlapping_docked) >= len(overlapping) - 1:
00176
00177
00178
00179
00180 parents = []
00181 for mw1 in overlapping_docked:
00182
00183 parent = mw1.parent()
00184 if parent is None:
00185 continue
00186
00187 parent = parent.parent()
00188 if parent is None:
00189 continue
00190 for mw2 in overlapping_docked:
00191 if mw2 == parent:
00192 parents.append(mw2)
00193 for parent in parents:
00194 overlapping_docked.remove(parent)
00195
00196
00197
00198 if len(overlapping_docked) == 1:
00199
00200 widget = overlapping_docked[0]
00201 else:
00202
00203 pass
00204
00205
00206 return widget
00207
00208 def _widget_contains(self, widget, point):
00209 topleft = widget.mapToGlobal(widget.mapFromParent(widget.geometry().topLeft()))
00210 rect = QRect(topleft, widget.geometry().size())
00211 return rect.contains(point)
00212
00213 def save_settings(self, settings):
00214
00215 settings.set_value('parent', self._parent_container_serial_number())
00216
00217 title_bar = self.titleBarWidget()
00218 title_bar.save_settings(settings)
00219
00220 def restore_settings(self, settings):
00221 serial_number = settings.value('parent', None)
00222
00223 if serial_number is not None:
00224 serial_number = int(serial_number)
00225 if self._parent_container_serial_number() != serial_number and self._container_manager is not None:
00226 floating = self.isFloating()
00227 pos = self.pos()
00228 new_container = self._container_manager.get_container(serial_number)
00229 if new_container is not None:
00230 new_parent = new_container.main_window
00231 else:
00232 new_parent = self._container_manager.get_root_main_window()
00233 area = self.parent().dockWidgetArea(self)
00234 new_parent.addDockWidget(area, self)
00235 if floating:
00236 self.setFloating(floating)
00237 self.move(pos)
00238
00239 title_bar = self.titleBarWidget()
00240 title_bar.restore_settings(settings)
00241
00242 def _parent_container(self, dock_widget):
00243 from .dock_widget_container import DockWidgetContainer
00244 parent = dock_widget.parent()
00245 if parent is not None:
00246 parent = parent.parent()
00247 if isinstance(parent, DockWidgetContainer):
00248 return parent
00249 return None
00250
00251 def _parent_container_serial_number(self):
00252 serial_number = None
00253 parent = self._parent_container(self)
00254 if parent is not None:
00255 serial_number = parent.serial_number()
00256 return serial_number