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 widget = widget.parent()
00139 return widget
00140
00141 def _widget_at(self, global_point):
00142
00143 widget = QApplication.widgetAt(global_point)
00144
00145 root_main_window = self._container_manager.get_root_main_window()
00146
00147
00148 if widget == root_main_window and not self._widget_contains(root_main_window, global_point):
00149
00150 widget = None
00151
00152
00153 if widget is None and self.isFloating():
00154
00155 overlapping = {}
00156 for main_window in self._main_windows:
00157 if self._widget_contains(main_window, global_point):
00158 parent = main_window.parent()
00159 is_floating = parent is None or parent.isFloating()
00160 overlapping[main_window] = is_floating
00161
00162
00163 if len(overlapping) == 1:
00164
00165 widget, _ = overlapping.popitem()
00166 elif len(overlapping) > 1:
00167
00168
00169 overlapping_docked = [mw for mw, floating in overlapping.iteritems() if not floating]
00170
00171
00172
00173 if len(overlapping_docked) >= len(overlapping) - 1:
00174
00175
00176
00177
00178 parents = []
00179 for mw1 in overlapping_docked:
00180
00181 parent = mw1.parent()
00182 if parent is None:
00183 continue
00184
00185 parent = parent.parent()
00186 if parent is None:
00187 continue
00188 for mw2 in overlapping_docked:
00189 if mw2 == parent:
00190 parents.append(mw2)
00191 for parent in parents:
00192 overlapping_docked.remove(parent)
00193
00194
00195
00196 if len(overlapping_docked) == 1:
00197
00198 widget = overlapping_docked[0]
00199 else:
00200
00201 pass
00202
00203
00204 return widget
00205
00206 def _widget_contains(self, widget, point):
00207 topleft = widget.mapToGlobal(widget.mapFromParent(widget.geometry().topLeft()))
00208 rect = QRect(topleft, widget.geometry().size())
00209 return rect.contains(point)
00210
00211 def save_settings(self, settings):
00212
00213 settings.set_value('parent', self._parent_container_serial_number())
00214
00215 title_bar = self.titleBarWidget()
00216 title_bar.save_settings(settings)
00217
00218 def restore_settings(self, settings):
00219 serial_number = settings.value('parent', None)
00220
00221 if serial_number is not None:
00222 serial_number = int(serial_number)
00223 if self._parent_container_serial_number() != serial_number and self._container_manager is not None:
00224 floating = self.isFloating()
00225 pos = self.pos()
00226 new_container = self._container_manager.get_container(serial_number)
00227 if new_container is not None:
00228 new_parent = new_container.main_window
00229 else:
00230 new_parent = self._container_manager.get_root_main_window()
00231 area = self.parent().dockWidgetArea(self)
00232 new_parent.addDockWidget(area, self)
00233 if floating:
00234 self.setFloating(floating)
00235 self.move(pos)
00236
00237 title_bar = self.titleBarWidget()
00238 title_bar.restore_settings(settings)
00239
00240 def _parent_container(self, dock_widget):
00241 from .dock_widget_container import DockWidgetContainer
00242 parent = dock_widget.parent()
00243 if parent is not None:
00244 parent = parent.parent()
00245 if isinstance(parent, DockWidgetContainer):
00246 return parent
00247 return None
00248
00249 def _parent_container_serial_number(self):
00250 serial_number = None
00251 parent = self._parent_container(self)
00252 if parent is not None:
00253 serial_number = parent.serial_number()
00254 return serial_number