diff --git a/FlatCAMApp.py b/FlatCAMApp.py
index b56df9e6..ba959213 100644
--- a/FlatCAMApp.py
+++ b/FlatCAMApp.py
@@ -1032,6 +1032,7 @@ class App(QtCore.QObject):
self.ui.menuview_zoom_in.triggered.connect(lambda: self.plotcanvas.zoom(1 / 1.5))
self.ui.menuview_zoom_out.triggered.connect(lambda: self.plotcanvas.zoom(1.5))
self.ui.menuview_toggle_fscreen.triggered.connect(self.on_fullscreen)
+ self.ui.menuview_toggle_parea.triggered.connect(self.on_toggle_plotarea)
self.ui.menuview_toggle_grid.triggered.connect(self.on_toggle_grid)
self.ui.menuview_toggle_axis.triggered.connect(self.on_toggle_axis)
self.ui.menuview_toggle_workspace.triggered.connect(self.on_workspace_menu)
@@ -2752,13 +2753,29 @@ class App(QtCore.QObject):
if self.toggle_fscreen is False:
for tb in self.ui.findChildren(QtWidgets.QToolBar):
tb.setVisible(False)
- self.ui.notebook.setVisible(False)
+ self.ui.splitter_left.setVisible(False)
self.toggle_fscreen = True
else:
self.restore_toolbar_view()
- self.ui.notebook.setVisible(True)
+ self.ui.splitter_left.setVisible(True)
self.toggle_fscreen = False
+ def on_toggle_plotarea(self):
+ try:
+ name = self.ui.plot_tab_area.widget(0).objectName()
+ except AttributeError:
+ self.ui.plot_tab_area.addTab(self.ui.plot_tab, "Plot Area")
+ # remove the close button from the Plot Area tab (first tab index = 0) as this one will always be ON
+ self.ui.plot_tab_area.protectTab(0)
+ return
+
+ if name != 'plotarea':
+ self.ui.plot_tab_area.insertTab(0, self.ui.plot_tab, "Plot Area")
+ # remove the close button from the Plot Area tab (first tab index = 0) as this one will always be ON
+ self.ui.plot_tab_area.protectTab(0)
+ else:
+ self.ui.plot_tab_area.closeTab(0)
+
def on_toggle_axis(self):
if self.toggle_axis is False:
self.plotcanvas.v_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
@@ -3751,6 +3768,10 @@ class App(QtCore.QObject):
if event.key == 'S':
self.on_file_saveproject()
+ # Toggle Plot Area
+ if event.key == 'F10':
+ self.on_toggle_plotarea()
+
return
elif self.key_modifiers == QtCore.Qt.AltModifier:
# place holder for further shortcut key
@@ -3791,6 +3812,7 @@ class App(QtCore.QObject):
if event.key == 'F10':
self.on_fullscreen()
+ return
elif self.key_modifiers == QtCore.Qt.ShiftModifier:
# place holder for further shortcut key
@@ -3825,7 +3847,6 @@ class App(QtCore.QObject):
if event.key == 'Y':
self.on_skewy()
- return
else:
if event.key == 'F1':
webbrowser.open(self.manual_url)
@@ -3918,6 +3939,17 @@ class App(QtCore.QObject):
def on_shortcut_list(self):
+ # add the tab if it was closed
+ self.ui.plot_tab_area.addTab(self.ui.shortcuts_tab, "Key Shortcut List")
+
+ # delete the absolute and relative position and messages in the infobar
+ self.ui.position_label.setText("")
+ self.ui.rel_position_label.setText("")
+
+ # Switch plot_area to preferences page
+ self.ui.plot_tab_area.setCurrentWidget(self.ui.shortcuts_tab)
+ self.ui.show()
+
msg = '''Shortcut list
~: Show Shortcut List
diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py
index c015143a..e3205ffe 100644
--- a/FlatCAMGUI.py
+++ b/FlatCAMGUI.py
@@ -268,6 +268,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.menuview.addSeparator()
self.menuview_toggle_fscreen = self.menuview.addAction(
QtGui.QIcon('share/fscreen32.png'), "&Toggle FullScreen\tALT+F10")
+ self.menuview_toggle_parea = self.menuview.addAction(
+ QtGui.QIcon('share/plot32.png'), "&Toggle Plot Area\tCTRL+F10")
self.menuview.addSeparator()
self.menuview_toggle_grid = self.menuview.addAction(QtGui.QIcon('share/grid32.png'), "&Toggle Grid\tG")
@@ -408,52 +410,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.addToolBar(self.geo_edit_toolbar)
self.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
self.snap_toolbar.setObjectName('Snap_TB')
+ # self.snap_toolbar.setMaximumHeight(30)
self.addToolBar(self.snap_toolbar)
- # if self.app.gui_defaults['global_theme'] == 'standard':
- # self.toolbarfile = QtWidgets.QToolBar('File Toolbar')
- # self.toolbarfile.setObjectName('File_TB')
- # self.addToolBar(self.toolbarfile)
- # self.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
- # self.toolbargeo.setObjectName('Edit_TB')
- # self.addToolBar(self.toolbargeo)
- # self.toolbarview = QtWidgets.QToolBar('View Toolbar')
- # self.toolbarview.setObjectName('View_TB')
- # self.addToolBar(self.toolbarview)
- # self.toolbartools = QtWidgets.QToolBar('Tools Toolbar')
- # self.toolbartools.setObjectName('Tools_TB')
- # self.addToolBar(self.toolbartools)
- # self.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar')
- # self.exc_edit_toolbar.setObjectName('ExcEditor_TB')
- # self.addToolBar(self.exc_edit_toolbar)
- # self.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar')
- # self.geo_edit_toolbar.setVisible(False)
- # self.geo_edit_toolbar.setObjectName('GeoEditor_TB')
- # self.addToolBar(self.geo_edit_toolbar)
- # self.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
- # self.snap_toolbar.setObjectName('Snap_TB')
- # elif self.app.defaults['global_theme'] == 'compact':
- # self.toolbarfile = QtWidgets.QToolBar('File Toolbar')
- # self.toolbarfile.setObjectName('File_TB')
- # self.addToolBar(self.toolbarfile)
- # self.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
- # self.toolbargeo.setObjectName('Edit_TB')
- # self.addToolBar(self.toolbargeo)
- # self.toolbarview = QtWidgets.QToolBar('View Toolbar')
- # self.toolbarview.setObjectName('View_TB')
- # self.addToolBar(self.toolbarview)
- # self.toolbartools = QtWidgets.QToolBar('Tools Toolbar')
- # self.toolbartools.setObjectName('Tools_TB')
- # self.addToolBar(self.toolbartools)
- # self.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar')
- # self.exc_edit_toolbar.setObjectName('ExcEditor_TB')
- # self.addToolBar(self.exc_edit_toolbar)
- # self.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar')
- # self.geo_edit_toolbar.setVisible(False)
- # self.geo_edit_toolbar.setObjectName('GeoEditor_TB')
- # self.addToolBar(self.geo_edit_toolbar)
- # self.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
- # self.snap_toolbar.setObjectName('Snap_TB')
- # self.addToolBar(self.snap_toolbar)
### File Toolbar ###
self.file_open_gerber_btn = self.toolbarfile.addAction(QtGui.QIcon('share/flatcam_icon32.png'),
@@ -580,15 +538,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.notebook = QtWidgets.QTabWidget()
self.splitter.addWidget(self.notebook)
- # if self.app.defaults['global_theme'] == 'standard':
- # self.splitter.addWidget(self.notebook)
- # elif self.app.defaults['global_theme'] == 'compact':
- # self.splitter_2 = QtWidgets.QSplitter(Qt.Vertical)
- # self.splitter.addWidget(self.splitter_2)
- # self.splitter_2.addWidget(self.notebook)
- # self.splitter_2.setHandleWidth(0)
- # self.snap_toolbar.setMaximumHeight(30)
- # self.splitter_2.addWidget(self.snap_toolbar)
+ self.splitter_left = QtWidgets.QSplitter(Qt.Vertical)
+ self.splitter.addWidget(self.splitter_left)
+ self.splitter_left.addWidget(self.notebook)
+ self.splitter_left.setHandleWidth(0)
### Project ###
self.project_tab = QtWidgets.QWidget()
@@ -620,16 +573,19 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.right_lay = QtWidgets.QVBoxLayout()
self.right_lay.setContentsMargins(0, 0, 0, 0)
self.right_widget.setLayout(self.right_lay)
- self.plot_tab_area = FCTab()
+ # self.plot_tab_area = FCTab()
+ self.plot_tab_area = FCDetachableTab()
+
self.right_lay.addWidget(self.plot_tab_area)
self.plot_tab_area.setTabsClosable(True)
- plot_tab = QtWidgets.QWidget()
- self.plot_tab_area.addTab(plot_tab, "Plot Area")
+ self.plot_tab = QtWidgets.QWidget()
+ self.plot_tab.setObjectName("plotarea")
+ self.plot_tab_area.addTab(self.plot_tab, "Plot Area")
self.right_layout = QtWidgets.QVBoxLayout()
self.right_layout.setContentsMargins(2, 2, 2, 2)
- plot_tab.setLayout(self.right_layout)
+ self.plot_tab.setLayout(self.right_layout)
# remove the close button from the Plot Area tab (first tab index = 0) as this one will always be ON
self.plot_tab_area.protectTab(0)
@@ -759,6 +715,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
"which is the file storing the working default preferences.")
self.pref_tab_bottom_layout_2.addWidget(self.pref_save_button)
+ ########################################
+ ### HERE WE BUILD THE SHORTCUTS LIST. TAB AREA ###
+ ########################################
+ self.shortcuts_tab = QtWidgets.QWidget()
+ self.sh_tab_layout = QtWidgets.QVBoxLayout(self.shortcuts_tab)
+ self.sh_tab_layout.setContentsMargins(2, 2, 2, 2)
+
##############################################################
### HERE WE BUILD THE CONTEXT MENU FOR RMB CLICK ON CANVAS ###
@@ -918,7 +881,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
# restore the Toolbar State from file
try:
- with open(self.app.data_path + '\state.config', 'rb') as stream:
+ with open(self.app.data_path + '\gui_state.config', 'rb') as stream:
self.restoreState(QtCore.QByteArray(stream.read()))
log.debug("FlatCAMGUI.__init__() --> UI state restored.")
except IOError:
@@ -992,7 +955,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
if self.app.should_we_quit is True:
# save toolbar state to file
- with open(self.app.data_path + '\state.config', 'wb') as stream:
+ with open(self.app.data_path + '\gui_state.config', 'wb') as stream:
stream.write(self.saveState().data())
log.debug("FlatCAMGUI.__init__() --> UI state saved.")
QtWidgets.qApp.quit()
diff --git a/GUIElements.py b/GUIElements.py
index 30a1e856..88decd41 100644
--- a/GUIElements.py
+++ b/GUIElements.py
@@ -1,4 +1,6 @@
-from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets
+from PyQt5 import QtGui, QtCore, QtWidgets
+from PyQt5.QtCore import pyqtSignal, pyqtSlot
+
from copy import copy
import re
import logging
@@ -550,6 +552,448 @@ class FCTab(QtWidgets.QTabWidget):
self.tabBar().setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
+class FCDetachableTab(QtWidgets.QTabWidget):
+ # From here: https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget
+ def __init__(self, parent=None):
+
+ super().__init__()
+
+ self.tabBar = self.FCTabBar(self)
+ self.tabBar.onDetachTabSignal.connect(self.detachTab)
+ self.tabBar.onMoveTabSignal.connect(self.moveTab)
+ self.tabBar.detachedTabDropSignal.connect(self.detachedTabDrop)
+
+ self.setTabBar(self.tabBar)
+
+ # Used to keep a reference to detached tabs since their QMainWindow
+ # does not have a parent
+ self.detachedTabs = {}
+
+ # Close all detached tabs if the application is closed explicitly
+ QtWidgets.qApp.aboutToQuit.connect(self.closeDetachedTabs) # @UndefinedVariable
+
+ self.setTabsClosable(True)
+ self.tabCloseRequested.connect(self.closeTab)
+
+
+ def deleteTab(self, currentIndex):
+ widget = self.widget(currentIndex)
+ if widget is not None:
+ widget.deleteLater()
+ self.removeTab(currentIndex)
+
+ def closeTab(self, currentIndex):
+ self.removeTab(currentIndex)
+
+ def protectTab(self, currentIndex):
+ # self.FCTabBar().setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
+ self.tabBar.setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
+
+ ##
+ # The default movable functionality of QTabWidget must remain disabled
+ # so as not to conflict with the added features
+ def setMovable(self, movable):
+ pass
+
+ ##
+ # Move a tab from one position (index) to another
+ #
+ # @param fromIndex the original index location of the tab
+ # @param toIndex the new index location of the tab
+ @pyqtSlot(int, int)
+ def moveTab(self, fromIndex, toIndex):
+ widget = self.widget(fromIndex)
+ icon = self.tabIcon(fromIndex)
+ text = self.tabText(fromIndex)
+
+ self.removeTab(fromIndex)
+ self.insertTab(toIndex, widget, icon, text)
+ self.setCurrentIndex(toIndex)
+
+
+ ##
+ # Detach the tab by removing it's contents and placing them in
+ # a DetachedTab window
+ #
+ # @param index the index location of the tab to be detached
+ # @param point the screen position for creating the new DetachedTab window
+ @pyqtSlot(int, QtCore.QPoint)
+ def detachTab(self, index, point):
+
+ # Get the tab content
+ name = self.tabText(index)
+ icon = self.tabIcon(index)
+ if icon.isNull():
+ icon = self.window().windowIcon()
+ contentWidget = self.widget(index)
+
+ try:
+ contentWidgetRect = contentWidget.frameGeometry()
+ except AttributeError:
+ return
+
+ # Create a new detached tab window
+ detachedTab = self.FCDetachedTab(name, contentWidget)
+ detachedTab.setWindowModality(QtCore.Qt.NonModal)
+ detachedTab.setWindowIcon(icon)
+ detachedTab.setGeometry(contentWidgetRect)
+ detachedTab.onCloseSignal.connect(self.attachTab)
+ detachedTab.onDropSignal.connect(self.tabBar.detachedTabDrop)
+ detachedTab.move(point)
+ detachedTab.show()
+
+
+ # Create a reference to maintain access to the detached tab
+ self.detachedTabs[name] = detachedTab
+
+
+ ##
+ # Re-attach the tab by removing the content from the DetachedTab window,
+ # closing it, and placing the content back into the DetachableTabWidget
+ #
+ # @param contentWidget the content widget from the DetachedTab window
+ # @param name the name of the detached tab
+ # @param icon the window icon for the detached tab
+ # @param insertAt insert the re-attached tab at the given index
+ def attachTab(self, contentWidget, name, icon, insertAt=None):
+
+ # Make the content widget a child of this widget
+ contentWidget.setParent(self)
+
+ # Remove the reference
+ del self.detachedTabs[name]
+
+ # Create an image from the given icon (for comparison)
+ if not icon.isNull():
+ try:
+ tabIconPixmap = icon.pixmap(icon.availableSizes()[0])
+ tabIconImage = tabIconPixmap.toImage()
+ except IndexError:
+ tabIconImage = None
+ else:
+ tabIconImage = None
+
+ # Create an image of the main window icon (for comparison)
+ if not icon.isNull():
+ try:
+ windowIconPixmap = self.window().windowIcon().pixmap(icon.availableSizes()[0])
+ windowIconImage = windowIconPixmap.toImage()
+ except IndexError:
+ windowIconImage = None
+ else:
+ windowIconImage = None
+
+ # Determine if the given image and the main window icon are the same.
+ # If they are, then do not add the icon to the tab
+ if tabIconImage == windowIconImage:
+ if insertAt == None:
+ index = self.addTab(contentWidget, name)
+ else:
+ index = self.insertTab(insertAt, contentWidget, name)
+ else:
+ if insertAt == None:
+ index = self.addTab(contentWidget, icon, name)
+ else:
+ index = self.insertTab(insertAt, contentWidget, icon, name)
+
+
+ # Make this tab the current tab
+ if index > -1:
+ self.setCurrentIndex(index)
+
+
+ ##
+ # Remove the tab with the given name, even if it is detached
+ #
+ # @param name the name of the tab to be removed
+ def removeTabByName(self, name):
+
+ # Remove the tab if it is attached
+ attached = False
+ for index in range(self.count()):
+ if str(name) == str(self.tabText(index)):
+ self.removeTab(index)
+ attached = True
+ break
+
+
+ # If the tab is not attached, close it's window and
+ # remove the reference to it
+ if not attached:
+ for key in self.detachedTabs:
+ if str(name) == str(key):
+ self.detachedTabs[key].onCloseSignal.disconnect()
+ self.detachedTabs[key].close()
+ del self.detachedTabs[key]
+ break
+
+
+ ##
+ # Handle dropping of a detached tab inside the DetachableTabWidget
+ #
+ # @param name the name of the detached tab
+ # @param index the index of an existing tab (if the tab bar
+ # determined that the drop occurred on an
+ # existing tab)
+ # @param dropPos the mouse cursor position when the drop occurred
+ @QtCore.pyqtSlot(str, int, QtCore.QPoint)
+ def detachedTabDrop(self, name, index, dropPos):
+
+ # If the drop occurred on an existing tab, insert the detached
+ # tab at the existing tab's location
+ if index > -1:
+
+ # Create references to the detached tab's content and icon
+ contentWidget = self.detachedTabs[name].contentWidget
+ icon = self.detachedTabs[name].windowIcon()
+
+ # Disconnect the detached tab's onCloseSignal so that it
+ # does not try to re-attach automatically
+ self.detachedTabs[name].onCloseSignal.disconnect()
+
+ # Close the detached
+ self.detachedTabs[name].close()
+
+ # Re-attach the tab at the given index
+ self.attachTab(contentWidget, name, icon, index)
+
+
+ # If the drop did not occur on an existing tab, determine if the drop
+ # occurred in the tab bar area (the area to the side of the QTabBar)
+ else:
+
+ # Find the drop position relative to the DetachableTabWidget
+ tabDropPos = self.mapFromGlobal(dropPos)
+
+ # If the drop position is inside the DetachableTabWidget...
+ if self.rect().contains(tabDropPos):
+
+ # If the drop position is inside the tab bar area (the
+ # area to the side of the QTabBar) or there are not tabs
+ # currently attached...
+ if tabDropPos.y() < self.tabBar.height() or self.count() == 0:
+
+ # Close the detached tab and allow it to re-attach
+ # automatically
+ self.detachedTabs[name].close()
+
+
+ ##
+ # Close all tabs that are currently detached.
+ def closeDetachedTabs(self):
+ listOfDetachedTabs = []
+
+ for key in self.detachedTabs:
+ listOfDetachedTabs.append(self.detachedTabs[key])
+
+ for detachedTab in listOfDetachedTabs:
+ detachedTab.close()
+
+
+ ##
+ # When a tab is detached, the contents are placed into this QMainWindow. The tab
+ # can be re-attached by closing the dialog or by dragging the window into the tab bar
+ class FCDetachedTab(QtWidgets.QMainWindow):
+ onCloseSignal = pyqtSignal(QtWidgets.QWidget, str, QtGui.QIcon)
+ onDropSignal = pyqtSignal(str, QtCore.QPoint)
+
+ def __init__(self, name, contentWidget):
+ QtWidgets.QMainWindow.__init__(self, None)
+
+ self.setObjectName(name)
+ self.setWindowTitle(name)
+
+ self.contentWidget = contentWidget
+ self.setCentralWidget(self.contentWidget)
+ self.contentWidget.show()
+
+ self.windowDropFilter = self.WindowDropFilter()
+ self.installEventFilter(self.windowDropFilter)
+ self.windowDropFilter.onDropSignal.connect(self.windowDropSlot)
+
+
+ ##
+ # Handle a window drop event
+ #
+ # @param dropPos the mouse cursor position of the drop
+ @QtCore.pyqtSlot(QtCore.QPoint)
+ def windowDropSlot(self, dropPos):
+ self.onDropSignal.emit(self.objectName(), dropPos)
+
+
+ ##
+ # If the window is closed, emit the onCloseSignal and give the
+ # content widget back to the DetachableTabWidget
+ #
+ # @param event a close event
+ def closeEvent(self, event):
+ self.onCloseSignal.emit(self.contentWidget, self.objectName(), self.windowIcon())
+
+
+ ##
+ # An event filter class to detect a QMainWindow drop event
+ class WindowDropFilter(QtCore.QObject):
+ onDropSignal = pyqtSignal(QtCore.QPoint)
+
+ def __init__(self):
+ QtCore.QObject.__init__(self)
+ self.lastEvent = None
+
+
+ ##
+ # Detect a QMainWindow drop event by looking for a NonClientAreaMouseMove (173)
+ # event that immediately follows a Move event
+ #
+ # @param obj the object that generated the event
+ # @param event the current event
+ def eventFilter(self, obj, event):
+
+ # If a NonClientAreaMouseMove (173) event immediately follows a Move event...
+ if self.lastEvent == QtCore.QEvent.Move and event.type() == 173:
+
+ # Determine the position of the mouse cursor and emit it with the
+ # onDropSignal
+ mouseCursor = QtGui.QCursor()
+ dropPos = mouseCursor.pos()
+ self.onDropSignal.emit(dropPos)
+ self.lastEvent = event.type()
+ return True
+
+ else:
+ self.lastEvent = event.type()
+ return False
+
+ class FCTabBar(QtWidgets.QTabBar):
+ onDetachTabSignal = pyqtSignal(int, QtCore.QPoint)
+ onMoveTabSignal = pyqtSignal(int, int)
+ detachedTabDropSignal = pyqtSignal(str, int, QtCore.QPoint)
+
+ def __init__(self, parent=None):
+ QtWidgets.QTabBar.__init__(self, parent)
+
+ self.setAcceptDrops(True)
+ self.setElideMode(QtCore.Qt.ElideRight)
+ self.setSelectionBehaviorOnRemove(QtWidgets.QTabBar.SelectLeftTab)
+
+ self.dragStartPos = QtCore.QPoint()
+ self.dragDropedPos = QtCore.QPoint()
+ self.mouseCursor = QtGui.QCursor()
+ self.dragInitiated = False
+
+
+ # Send the onDetachTabSignal when a tab is double clicked
+ #
+ # @param event a mouse double click event
+ def mouseDoubleClickEvent(self, event):
+ event.accept()
+ self.onDetachTabSignal.emit(self.tabAt(event.pos()), self.mouseCursor.pos())
+
+
+ # Set the starting position for a drag event when the mouse button is pressed
+ #
+ # @param event a mouse press event
+ def mousePressEvent(self, event):
+ if event.button() == QtCore.Qt.LeftButton:
+ self.dragStartPos = event.pos()
+
+ self.dragDropedPos.setX(0)
+ self.dragDropedPos.setY(0)
+
+ self.dragInitiated = False
+
+ QtWidgets.QTabBar.mousePressEvent(self, event)
+
+
+ # Determine if the current movement is a drag. If it is, convert it into a QDrag. If the
+ # drag ends inside the tab bar, emit an onMoveTabSignal. If the drag ends outside the tab
+ # bar, emit an onDetachTabSignal.
+ #
+ # @param event a mouse move event
+ def mouseMoveEvent(self, event):
+
+ # Determine if the current movement is detected as a drag
+ if not self.dragStartPos.isNull() and ((event.pos() - self.dragStartPos).manhattanLength() < QtWidgets.QApplication.startDragDistance()):
+ self.dragInitiated = True
+
+ # If the current movement is a drag initiated by the left button
+ if (((event.buttons() & QtCore.Qt.LeftButton)) and self.dragInitiated):
+
+ # Stop the move event
+ finishMoveEvent = QtGui.QMouseEvent(QtCore.QEvent.MouseMove, event.pos(), QtCore.Qt.NoButton, QtCore.Qt.NoButton, QtCore.Qt.NoModifier)
+ QtWidgets.QTabBar.mouseMoveEvent(self, finishMoveEvent)
+
+ # Convert the move event into a drag
+ drag = QtGui.QDrag(self)
+ mimeData = QtCore.QMimeData()
+ # mimeData.setData('action', 'application/tab-detach')
+ drag.setMimeData(mimeData)
+ # screen = QScreen(self.parentWidget().currentWidget().winId())
+ # Create the appearance of dragging the tab content
+ pixmap = self.parent().widget(self.tabAt(self.dragStartPos)).grab()
+ targetPixmap = QtGui.QPixmap(pixmap.size())
+ targetPixmap.fill(QtCore.Qt.transparent)
+ painter = QtGui.QPainter(targetPixmap)
+ painter.setOpacity(0.85)
+ painter.drawPixmap(0, 0, pixmap)
+ painter.end()
+ drag.setPixmap(targetPixmap)
+
+ # Initiate the drag
+ dropAction = drag.exec_(QtCore.Qt.MoveAction | QtCore.Qt.CopyAction)
+
+
+ # For Linux: Here, drag.exec_() will not return MoveAction on Linux. So it
+ # must be set manually
+ if self.dragDropedPos.x() != 0 and self.dragDropedPos.y() != 0:
+ dropAction = QtCore.Qt.MoveAction
+
+
+ # If the drag completed outside of the tab bar, detach the tab and move
+ # the content to the current cursor position
+ if dropAction == QtCore.Qt.IgnoreAction:
+ event.accept()
+ self.onDetachTabSignal.emit(self.tabAt(self.dragStartPos), self.mouseCursor.pos())
+
+ # Else if the drag completed inside the tab bar, move the selected tab to the new position
+ elif dropAction == QtCore.Qt.MoveAction:
+ if not self.dragDropedPos.isNull():
+ event.accept()
+ self.onMoveTabSignal.emit(self.tabAt(self.dragStartPos), self.tabAt(self.dragDropedPos))
+ else:
+ QtWidgets.QTabBar.mouseMoveEvent(self, event)
+
+ # Determine if the drag has entered a tab position from another tab position
+ #
+ # @param event a drag enter event
+ def dragEnterEvent(self, event):
+ mimeData = event.mimeData()
+ # formats = mcd imeData.formats()
+
+ # if formats.contains('action') and mimeData.data('action') == 'application/tab-detach':
+ # event.acceptProposedAction()
+
+ QtWidgets.QTabBar.dragMoveEvent(self, event)
+
+ # Get the position of the end of the drag
+ #
+ # @param event a drop event
+ def dropEvent(self, event):
+ self.dragDropedPos = event.pos()
+ QtWidgets.QTabBar.dropEvent(self, event)
+
+
+
+ # Determine if the detached tab drop event occurred on an existing tab,
+ # then send the event to the DetachableTabWidget
+ def detachedTabDrop(self, name, dropPos):
+
+ tabDropPos = self.mapFromGlobal(dropPos)
+
+ index = self.tabAt(tabDropPos)
+
+ self.detachedTabDropSignal.emit(name, index, dropPos)
+
+
class VerticalScrollArea(QtWidgets.QScrollArea):
"""
This widget extends QtGui.QScrollArea to make a vertical-only
diff --git a/ObjectCollection.py b/ObjectCollection.py
index 29bf7e14..9d8f4b6a 100644
--- a/ObjectCollection.py
+++ b/ObjectCollection.py
@@ -273,6 +273,11 @@ class ObjectCollection(QtCore.QAbstractItemModel):
if key == QtCore.Qt.Key_S:
self.app.on_file_saveproject()
+
+ # Toggle Plot Area
+ if key == QtCore.Qt.Key_F10:
+ self.app.on_toggle_plotarea()
+
return
elif modifiers == QtCore.Qt.ShiftModifier:
@@ -324,6 +329,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
if key == QtCore.Qt.Key_Y:
self.app.on_skewy()
return
+
elif modifiers == QtCore.Qt.AltModifier:
# Eanble all plots
if key == Qt.Key_1:
diff --git a/README.md b/README.md
index 73f8b130..3d411bf8 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,8 @@ CAD program, and create G-Code for Isolation routing.
- updated the camlib.CNCJob.scale() function so now the GCode is scaled also (quite a HACK :( it will need to be replaced at some point)). Units change work now on the GCODE also.
- added the bounds coordinates to the GCODE header
- FlatCAM saves now to a file in self.data_path the toolbar positions and the position of TCL Shell
+- Plot Area Tab view can now be toggled, added entry in View Menu and shortcut key CTRL+F10
+- All the tabs in the GUI right side are (Plot Are, Preferences etc) are now detachable to a separate windows which when closed it returns in the previous location in the toolbar. Those detached tabs can be also reattached by drag and drop.
30.01.2019
diff --git a/share/plot32.png b/share/plot32.png
new file mode 100644
index 00000000..8bab9afc
Binary files /dev/null and b/share/plot32.png differ