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