diff --git a/SelectorToolbar_Gui.py b/SelectorToolbar_Gui.py new file mode 100644 index 0000000..a0f41f4 --- /dev/null +++ b/SelectorToolbar_Gui.py @@ -0,0 +1,475 @@ +# Selector toolbar for FreeCAD +# Copyright (C) 2015, 2016 (as part of TabBar) triplus @ FreeCAD +# Copyright (C) 2017 triplus @ FreeCAD +# +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +"""Selector toolbar for FreeCAD.""" + +import os +import FreeCADGui as Gui +import FreeCAD as App +from PySide import QtGui +from PySide import QtCore + +actions = {} +mw = Gui.getMainWindow() +aMenu = QtGui.QAction(mw) +aMenu.setObjectName("SelectorToolbarMenu") +sMenu = QtGui.QMenu() +aMenu.setMenu(sMenu) +group = QtGui.QActionGroup(mw) +dList = "ArchWorkbench,PartDesignWorkbench" +path = os.path.dirname(__file__) + "/Resources/icons/" +p = App.ParamGet("User parameter:BaseApp/SelectorToolbar") + + +def onSelector(a): + """Activate workbench on selection.""" + Gui.doCommand('Gui.activateWorkbench("' + a.data() + '")') + + +def wbIcon(i): + """Create workbench icon.""" + if str(i.find("XPM")) != "-1": + icon = [] + for a in ((((i + .split('{', 1)[1]) + .rsplit('}', 1)[0]) + .strip()) + .split("\n")): + icon.append((a + .split('"', 1)[1]) + .rsplit('"', 1)[0]) + icon = QtGui.QIcon(QtGui.QPixmap(icon)) + else: + icon = QtGui.QIcon(QtGui.QPixmap(i)) + if icon.isNull(): + icon = QtGui.QIcon.fromTheme("freecad") + return icon + + +def wbActions(): + """Create workbench actions.""" + wbList = Gui.listWorkbenches() + for i in wbList: + if i not in actions: + action = QtGui.QAction(group) + action.setCheckable(True) + action.setText(wbList[i].MenuText) + action.setData(i) + try: + action.setIcon(wbIcon(wbList[i].Icon)) + except: + action.setIcon(QtGui.QIcon.fromTheme("freecad")) + actions[i] = action + + +def onOrientationChanged(): + """Style buttons based on the toolbar orientation.""" + if tb.orientation() == QtCore.Qt.Orientation.Horizontal: + for b in tb.findChildren(QtGui.QToolButton): + b.setProperty("toolbar_orientation", "horizontal") + b.style().polish(b) + else: + for b in tb.findChildren(QtGui.QToolButton): + b.setProperty("toolbar_orientation", "vertical") + b.style().polish(b) + + +def onStyle(): + """Manage the toolbutton style.""" + if p.GetString("Style") == "IconText": + tb.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) + elif p.GetString("Style") == "Text": + tb.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) + else: + tb.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly) + + +def selectorMenu(): + """Selector button with menu.""" + sMenu.clear() + static = p.GetBool("Static", 0) + enabled = p.GetString("Enabled") + partially = p.GetString("Partially") + unchecked = p.GetString("Unchecked") + position = p.GetString("Position") + enabled = enabled.split(",") + partially = partially.split(",") + unchecked = unchecked.split(",") + position = position.split(",") + active = Gui.activeWorkbench().__class__.__name__ + l = [] + for i in partially: + if i in actions: + l.append(i) + s = list(actions) + s.sort() + for i in s: + if i not in l and i not in enabled and i not in unchecked: + l.append(i) + if active not in l: + l.insert(0, active) + for i in l: + sMenu.addAction(actions[i]) + if static: + aMenu.setText("") + aMenu.setToolTip("Additional workbenches") + aMenu.setIcon(QtGui.QIcon(path + "SelectorToolbarMenu.svg")) + else: + sMenu.setDefaultAction(actions[active]) + aMenu.setText(actions[active].text()) + aMenu.setIcon(actions[active].icon()) + + +def onWorkbenchActivated(): + """Populate the selector toolbar.""" + tb.clear() + wbActions() + selectorMenu() + static = p.GetBool("Static", 0) + menu = p.GetString("Menu", "Front") + enabled = p.GetString("Enabled", dList) + enabled = enabled.split(",") + active = Gui.activeWorkbench().__class__.__name__ + if active not in enabled: + enabled.append(active) + if menu == "Front" and not static: + enabled.remove(active) + elif menu == "End" and not static: + enabled.remove(active) + else: + pass + if menu == "Front": + tb.addAction(aMenu) + w = tb.widgetForAction(aMenu) + w.setPopupMode(QtGui.QToolButton + .ToolButtonPopupMode + .InstantPopup) + group.blockSignals(True) + for i in enabled: + for key in actions: + if i == actions[key].data(): + a = actions[key] + a.setChecked(False) + tb.addAction(a) + if active == a.data(): + a.setChecked(True) + group.blockSignals(False) + if menu == "End": + tb.addAction(aMenu) + w = tb.widgetForAction(aMenu) + w.setPopupMode(QtGui.QToolButton + .ToolButtonPopupMode + .InstantPopup) + + +def prefDialog(): + """Preferences dialog.""" + dialog = QtGui.QDialog(mw) + dialog.resize(800, 450) + dialog.setWindowTitle("Selector toolbar") + layout = QtGui.QVBoxLayout() + dialog.setLayout(layout) + selector = QtGui.QListWidget(dialog) + selector.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + btnClose = QtGui.QPushButton("Close", dialog) + btnClose.setToolTip("Close the preferences dialog") + btnClose.setDefault(True) + btnUp = QtGui.QToolButton(dialog) + btnUp.setToolTip("Move selected item up") + btnUp.setArrowType(QtCore.Qt.UpArrow) + btnDown = QtGui.QToolButton(dialog) + btnDown.setToolTip("Move selected item down") + btnDown.setArrowType(QtCore.Qt.DownArrow) + l0 = QtGui.QVBoxLayout() + g0 = QtGui.QGroupBox("Style:") + g0.setLayout(l0) + r0 = QtGui.QRadioButton("Icon", g0) + r0.setObjectName("Icon") + r0.setToolTip("Toolbar button icon style") + r1 = QtGui.QRadioButton("Text", g0) + r1.setObjectName("Text") + r1.setToolTip("Toolbar button text style") + r2 = QtGui.QRadioButton("Icon and text", g0) + r2.setObjectName("IconText") + r2.setToolTip("Toolbar button icon and text style") + l0.addWidget(r0) + l0.addWidget(r1) + l0.addWidget(r2) + l1 = QtGui.QVBoxLayout() + g1 = QtGui.QGroupBox("Menu:") + g1.setLayout(l1) + r3 = QtGui.QRadioButton("Disabled", g1) + r3.setObjectName("Off") + r3.setToolTip("Disable selector menu") + r4 = QtGui.QRadioButton("Front", g1) + r4.setObjectName("Front") + r4.setToolTip("Selector menu at front") + r5 = QtGui.QRadioButton("End", g1) + r5.setObjectName("End") + r5.setToolTip("Selector menu at end") + c1 = QtGui.QCheckBox("Static") + c1.setToolTip("Do not adapt selector menu to active workbench") + l1.addWidget(r3) + l1.addWidget(r4) + l1.addWidget(r5) + l1.addWidget(c1) + l2 = QtGui.QHBoxLayout() + l2.addWidget(btnUp) + l2.addWidget(btnDown) + l2.addStretch(1) + l2.addWidget(btnClose) + l3 = QtGui.QHBoxLayout() + l3.addStretch() + l4 = QtGui.QVBoxLayout() + l4.addWidget(g0) + l4.addWidget(g1) + l4.addStretch() + l4.insertLayout(0, l3) + l5 = QtGui.QHBoxLayout() + l5.addWidget(selector) + l5.insertLayout(1, l4) + layout.insertLayout(0, l5) + layout.insertLayout(1, l2) + + def onAccepted(): + """Close dialog on button close.""" + dialog.done(1) + + def onFinished(): + """Delete dialog on close.""" + dialog.deleteLater() + + def onItemChanged(item=None): + """Save workbench list state.""" + if item: + selector.blockSignals(True) + if item.data(50) == "Unchecked": + item.setCheckState(QtCore.Qt.CheckState(1)) + item.setData(50, "Partially") + elif item.data(50) == "Partially": + item.setCheckState(QtCore.Qt.CheckState(2)) + item.setData(50, "Checked") + else: + item.setCheckState(QtCore.Qt.CheckState(0)) + item.setData(50, "Unchecked") + selector.blockSignals(False) + enabled = [] + partially = [] + unchecked = [] + for index in range(selector.count()): + if selector.item(index).checkState() == QtCore.Qt.Checked: + enabled.append(selector.item(index).data(32)) + elif selector.item(index).checkState() == QtCore.Qt.PartiallyChecked: + partially.append(selector.item(index).data(32)) + else: + unchecked.append(selector.item(index).data(32)) + p.SetString("Enabled", ",".join(enabled)) + p.SetString("Partially", ",".join(partially)) + p.SetString("Unchecked", ",".join(unchecked)) + onWorkbenchActivated() + + def onUp(): + """Save workbench position list.""" + currentIndex = selector.currentRow() + if currentIndex != 0: + selector.blockSignals(True) + currentItem = selector.takeItem(currentIndex) + selector.insertItem(currentIndex - 1, currentItem) + selector.setCurrentRow(currentIndex - 1) + selector.blockSignals(False) + position = [] + for index in range(selector.count()): + position.append(selector.item(index).data(32)) + p.SetString("Position", ",".join(position)) + onItemChanged() + + def onDown(): + """Save workbench position list.""" + currentIndex = selector.currentRow() + if currentIndex != selector.count() - 1 and currentIndex != -1: + selector.blockSignals(True) + currentItem = selector.takeItem(currentIndex) + selector.insertItem(currentIndex + 1, currentItem) + selector.setCurrentRow(currentIndex + 1) + selector.blockSignals(False) + position = [] + for index in range(selector.count()): + position.append(selector.item(index).data(32)) + p.SetString("Position", ",".join(position)) + onItemChanged() + + def onG0(r): + """Set toolbar button style.""" + if r: + for i in g0.findChildren(QtGui.QRadioButton): + if i.isChecked(): + p.SetString("Style", i.objectName()) + onStyle() + onWorkbenchActivated() + + def onG1(r): + """Manage the selector menu.""" + if r: + for i in g1.findChildren(QtGui.QRadioButton): + if i.isChecked(): + p.SetString("Menu", i.objectName()) + if i.objectName() == "Off": + c1.setEnabled(False) + else: + c1.setEnabled(True) + onWorkbenchActivated() + + def onC1(c): + """Manage the static mode.""" + if c: + p.SetBool("Static", 1) + else: + p.SetBool("Static", 0) + onWorkbenchActivated() + + enabled = p.GetString("Enabled", dList) + unchecked = p.GetString("Unchecked") + position = p.GetString("Position") + enabled = enabled.split(",") + unchecked = unchecked.split(",") + position = position.split(",") + s = list(actions) + s.sort() + for i in s: + if i not in position: + position.append(i) + for i in position: + if i in actions: + item = QtGui.QListWidgetItem(selector) + item.setText(actions[i].text()) + item.setIcon(actions[i].icon()) + item.setData(32, actions[i].data()) + if actions[i].data() in enabled: + item.setCheckState(QtCore.Qt.CheckState(2)) + item.setData(50, "Checked") + elif actions[i].data() in unchecked: + item.setCheckState(QtCore.Qt.CheckState(0)) + item.setData(50, "Unchecked") + else: + item.setCheckState(QtCore.Qt.CheckState(1)) + item.setData(50, "Partially") + style = p.GetString("Style") + if style == "Text": + r1.setChecked(True) + elif style == "IconText": + r2.setChecked(True) + else: + r0.setChecked(True) + menu = p.GetString("Menu", "Front") + if menu == "Front": + r4.setChecked(True) + elif menu == "End": + r5.setChecked(True) + else: + r3.setChecked(True) + c1.setEnabled(False) + static = p.GetBool("Static", 0) + if static: + c1.setChecked(True) + r0.toggled.connect(onG0) + r1.toggled.connect(onG0) + r2.toggled.connect(onG0) + r3.toggled.connect(onG1) + r4.toggled.connect(onG1) + r5.toggled.connect(onG1) + c1.stateChanged.connect(onC1) + btnUp.clicked.connect(onUp) + btnDown.clicked.connect(onDown) + selector.itemChanged.connect(onItemChanged) + dialog.finished.connect(onFinished) + btnClose.clicked.connect(onAccepted) + return dialog + + +def onPreferences(): + """Open the preferences dialog.""" + dialog = prefDialog() + dialog.show() + + +def accessoriesMenu(): + """Add selector toolbar preferences to accessories menu.""" + pref = QtGui.QAction(mw) + pref.setText("Selector toolbar") + pref.setObjectName("SelectorToolbar") + pref.triggered.connect(onPreferences) + try: + import AccessoriesMenu + AccessoriesMenu.addItem("SelectorToolbar") + except ImportError: + a = mw.findChild(QtGui.QAction, "AccessoriesMenu") + if a: + a.menu().addAction(pref) + else: + mb = mw.menuBar() + actionAccessories = QtGui.QAction(mw) + actionAccessories.setObjectName("AccessoriesMenu") + actionAccessories.setIconText("Accessories") + menu = QtGui.QMenu() + actionAccessories.setMenu(menu) + menu.addAction(pref) + + def addMenu(): + """Add accessories menu to the menu bar.""" + toolsMenu = mb.findChild(QtGui.QMenu, "&Tools") + if toolsMenu: + toolsMenu.addAction(actionAccessories) + addMenu() + mw.workbenchActivated.connect(addMenu) + + +def onClose(): + """Remove selector toolbar on FreeCAD close.""" + pathTB = "User parameter:BaseApp/Workbench/Global/Toolbar" + pTB = App.ParamGet(pathTB) + pTB.RemGroup("Selector") + + +def onStart(): + """Start selector toolbar.""" + start = False + try: + mw.mainWindowClosed + mw.workbenchActivated + global tb + tb = mw.findChild(QtGui.QToolBar, "Selector") + tb.orientationChanged + start = True + except AttributeError: + pass + if start: + t.stop() + t.deleteLater() + onStyle() + accessoriesMenu() + onWorkbenchActivated() + group.triggered.connect(onSelector) + mw.mainWindowClosed.connect(onClose) + mw.workbenchActivated.connect(onWorkbenchActivated) + tb.orientationChanged.connect(onOrientationChanged) + + +t = QtCore.QTimer() +t.timeout.connect(onStart) +t.start(500)