diff --git a/InitGui.py b/InitGui.py
index bd5ca57..999a972 100644
--- a/InitGui.py
+++ b/InitGui.py
@@ -6,66 +6,34 @@ wax = None
sea = None
tbr = None
+# Define the translation
+translate = App.Qt.translate
+
def QT_TRANSLATE_NOOP(context, text):
return text
def addToolSearchBox():
- import FreeCADGui
- from PySide import QtGui
- import SearchBoxLight
-
- # Define the translation
- translate = App.Qt.translate
-
global wax, sea, tbr
- mw = FreeCADGui.getMainWindow()
+ mw = Gui.getMainWindow()
+ import SearchBox
+ from PySide6.QtWidgets import QToolBar
+ from PySide6.QtGui import QShortcut, QKeySequence
+
if mw:
if sea is None:
- sea = SearchBoxLight.SearchBoxLight(
- getItemGroups=lambda: __import__("GetItemGroups").getItemGroups(),
- getToolTip=lambda groupId, setParent: __import__(
- "GetItemGroups"
- ).getToolTip(groupId, setParent),
- getItemDelegate=lambda: __import__(
- "IndentedItemDelegate"
- ).IndentedItemDelegate(),
- )
- sea.resultSelected.connect(
- lambda index, groupId: __import__("GetItemGroups").onResultSelected(
- index, groupId
- )
- )
-
- if wax is None:
- wax = QtGui.QWidgetAction(None)
- wax.setWhatsThis(
- translate(
- "SearchBar",
- "Use this search bar to find tools, document objects, preferences and more",
- )
- )
-
- sea.setWhatsThis(
- translate(
- "SearchBar",
- "Use this search bar to find tools, document objects, preferences and more",
- )
- )
- wax.setDefaultWidget(sea)
- ##mbr.addWidget(sea)
- # mbr.addAction(wax)
+ wax = SearchBox.SearchBoxFunction(mw)
if tbr is None:
- tbr = QtGui.QToolBar("SearchBar") # QtGui.QDockWidget()
- # Include FreeCAD in the name so that one can find windows labeled with FreeCAD easily in window managers which allow search through the list of open windows.
+ tbr = QToolBar("SearchBar") # QtGui.QDockWidget()
+ # Include FreeCAD in the name so that one can find windows labeled with
+ # FreeCAD easily in window managers which allow search through the list of open windows.
tbr.setObjectName("SearchBar")
tbr.addAction(wax)
mw.addToolBar(tbr)
tbr.show()
+ return
addToolSearchBox()
-import FreeCADGui
-
-FreeCADGui.getMainWindow().workbenchActivated.connect(addToolSearchBox)
+Gui.getMainWindow().workbenchActivated.connect(addToolSearchBox)
diff --git a/RefreshTools.py b/RefreshTools.py
index 8c08dc8..acd8d15 100644
--- a/RefreshTools.py
+++ b/RefreshTools.py
@@ -1,41 +1,49 @@
import os
import FreeCAD as App
+import FreeCADGui as Gui
+import StyleMapping_SearchBar
# Define the translation
translate = App.Qt.translate
def loadAllWorkbenches():
- from PySide import QtGui
- import FreeCADGui
+ import FreeCADGui as Gui
+ from PySide.QtGui import QLabel
+ from PySide.QtCore import Qt, SIGNAL, Signal, QObject, QThread, QSize
+ from PySide.QtGui import QIcon, QPixmap, QAction, QGuiApplication
+
+ activeWorkbench = Gui.activeWorkbench().name()
+ lbl = QLabel(translate("SearchBar", "Loading workbench … (…/…)"))
+ lbl.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
+
+ # Get the stylesheet from the main window and use it for this form
+ lbl.setStyleSheet("background-color: " + StyleMapping_SearchBar.ReturnStyleItem("Background_Color") + ";")
+
+ # # Get the main window from FreeCAD
+ # mw = Gui.getMainWindow()
+ # # Center the widget
+ # cp = QGuiApplication.screenAt(mw.pos()).geometry().center()
+ # lbl.move(cp)
- activeWorkbench = FreeCADGui.activeWorkbench().name()
- lbl = QtGui.QLabel(translate("SearchBar", "Loading workbench … (…/…)"))
lbl.show()
- lst = FreeCADGui.listWorkbenches()
+ lst = Gui.listWorkbenches()
for i, wb in enumerate(lst):
- msg = (
- translate("SearchBar", "Loading workbench ")
- + wb
- + " ("
- + str(i)
- + "/"
- + str(len(lst))
- + ")"
- )
+ msg = translate("SearchBar", "Loading workbench ") + wb + " (" + str(i + 1) + "/" + str(len(lst)) + ")"
print(msg)
lbl.setText(msg)
geo = lbl.geometry()
geo.setSize(lbl.sizeHint())
lbl.setGeometry(geo)
lbl.repaint()
- FreeCADGui.updateGui() # Probably slower with this, because it redraws the entire GUI with all tool buttons changed etc. but allows the label to actually be updated, and it looks nice and gives a quick overview of all the workbenches…
+ Gui.updateGui() # Probably slower with this, because it redraws the entire GUI with all tool buttons changed etc. but allows the label to actually be updated, and it looks nice and gives a quick overview of all the workbenches…
try:
- FreeCADGui.activateWorkbench(wb)
- except:
+ Gui.activateWorkbench(wb)
+ except Exception:
pass
lbl.hide()
- FreeCADGui.activateWorkbench(activeWorkbench)
+ Gui.activateWorkbench(activeWorkbench)
+ return
def cachePath():
@@ -91,23 +99,25 @@ def refreshToolbars(doLoadAllWorkbenches=True):
def refreshToolsAction():
- from PySide import QtGui
+ from PySide.QtWidgets import QApplication, QMessageBox
+ from PySide.QtCore import Qt
print("Refresh cached results")
- fw = QtGui.QApplication.focusWidget()
- if fw is not None:
- fw.clearFocus()
- reply = QtGui.QMessageBox.question(
- None,
+ msgBox = QMessageBox()
+ msgBox.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
+ # Get the main window from FreeCAD
+ mw = Gui.getMainWindow()
+ reply = msgBox.question(
+ mw,
translate("SearchBar", "Load all workbenches?"),
translate(
"SearchBar",
"""Load all workbenches? This can cause FreeCAD to become unstable, and this "reload tools" feature contained a bug that crashed freecad systematically, so please make sure you save your work before. It\'s a good idea to restart FreeCAD after this operation.""",
),
- QtGui.QMessageBox.Yes,
- QtGui.QMessageBox.No,
+ QMessageBox.Yes,
+ QMessageBox.No,
)
- if reply == QtGui.QMessageBox.Yes:
+ if reply == QMessageBox.Yes:
refreshToolbars()
else:
print("cancelled")
diff --git a/SearchBox.py b/SearchBox.py
index 9ac8bbf..e3160a0 100644
--- a/SearchBox.py
+++ b/SearchBox.py
@@ -27,6 +27,7 @@ from PySide.QtWidgets import (
QVBoxLayout,
QApplication,
QListWidget,
+ QWidgetAction,
)
from PySide.QtGui import (
QIcon,
@@ -48,6 +49,11 @@ globalIgnoreFocusOut = False
# Define the translation
translate = App.Qt.translate
+# Avoid garbage collection by storing the action in a global variable
+wax = None
+sea = None
+tbr = None
+
def easyToolTipWidget(html):
foo = QTextEdit()
@@ -57,6 +63,41 @@ def easyToolTipWidget(html):
return foo
+def SearchBoxFunction(mw):
+ import SearchBoxLight
+
+ global wax, sea, tbr
+
+ if mw:
+ if sea is None:
+ sea = SearchBoxLight.SearchBoxLight(
+ getItemGroups=lambda: __import__("GetItemGroups").getItemGroups(),
+ getToolTip=lambda groupId, setParent: __import__("GetItemGroups").getToolTip(groupId, setParent),
+ getItemDelegate=lambda: __import__("IndentedItemDelegate").IndentedItemDelegate(),
+ )
+ sea.resultSelected.connect(
+ lambda index, groupId: __import__("GetItemGroups").onResultSelected(index, groupId)
+ )
+
+ if wax is None:
+ wax = QWidgetAction(None)
+ wax.setWhatsThis(
+ translate(
+ "SearchBar",
+ "Use this search bar to find tools, document objects, preferences and more",
+ )
+ )
+
+ sea.setWhatsThis(
+ translate(
+ "SearchBar",
+ "Use this search bar to find tools, document objects, preferences and more",
+ )
+ )
+ wax.setDefaultWidget(sea)
+ return wax
+
+
class SearchBox(QLineEdit):
# The following block of code is present in the lightweight proxy SearchBoxLight
"""
@@ -91,9 +132,7 @@ class SearchBox(QLineEdit):
self.getItemGroups = getItemGroups
self.getToolTip = getToolTip
self.itemGroups = None # Will be initialized by calling getItemGroups() the first time the search box gains focus, through focusInEvent and refreshItemGroups
- self.maxVisibleRows = (
- maxVisibleRows # TODO: use this to compute the correct height
- )
+ self.maxVisibleRows = maxVisibleRows # TODO: use this to compute the correct height
# Create proxy model
self.proxyModel = QIdentityProxyModel()
# Filtered model to which items are manually added. Store it as a property of the object instead of a local variable, to prevent grbage collection.
@@ -105,9 +144,7 @@ class SearchBox(QLineEdit):
self.listView.setWindowFlag(Qt.WindowType.FramelessWindowHint)
self.listView.setSelectionMode(self.listView.SelectionMode.SingleSelection)
self.listView.setModel(self.proxyModel)
- self.listView.setItemDelegate(
- getItemDelegate()
- ) # https://stackoverflow.com/a/65930408/324969
+ self.listView.setItemDelegate(getItemDelegate()) # https://stackoverflow.com/a/65930408/324969
self.listView.setMouseTracking(True)
# make the QListView non-editable
self.listView.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
@@ -137,18 +174,10 @@ class SearchBox(QLineEdit):
# Note: should probably use the eventFilter method instead...
wdgctx = Qt.ShortcutContext.WidgetShortcut
- QShortcut(
- QKeySequence(Qt.Key.Key_Down), self, context=wdgctx
- ).activated.connect(self.listDown)
- QShortcut(QKeySequence(Qt.Key.Key_Up), self, context=wdgctx).activated.connect(
- self.listUp
- )
- QShortcut(
- QKeySequence(Qt.Key.Key_PageDown), self, context=wdgctx
- ).activated.connect(self.listPageDown)
- QShortcut(
- QKeySequence(Qt.Key.Key_PageUp), self, context=wdgctx
- ).activated.connect(self.listPageUp)
+ QShortcut(QKeySequence(Qt.Key.Key_Down), self, context=wdgctx).activated.connect(self.listDown)
+ QShortcut(QKeySequence(Qt.Key.Key_Up), self, context=wdgctx).activated.connect(self.listUp)
+ QShortcut(QKeySequence(Qt.Key.Key_PageDown), self, context=wdgctx).activated.connect(self.listPageDown)
+ QShortcut(QKeySequence(Qt.Key.Key_PageUp), self, context=wdgctx).activated.connect(self.listPageUp)
# Home and End do not work, for some reason.
# QShortcut(QKeySequence.MoveToEndOfDocument, self, context = wdgctx).activated.connect(self.listEnd)
@@ -156,25 +185,13 @@ class SearchBox(QLineEdit):
# QShortcut(QKeySequence(Qt.Key.Key_End), self, context = wdgctx).activated.connect(self.listEnd)
# QShortcut(QKeySequence('Home'), self, context = wdgctx).activated.connect(self.listStart)
- QShortcut(
- QKeySequence(Qt.Key.Key_Enter), self, context=wdgctx
- ).activated.connect(self.listAccept)
- QShortcut(
- QKeySequence(Qt.Key.Key_Return), self, context=wdgctx
- ).activated.connect(self.listAccept)
- QShortcut(QKeySequence("Ctrl+Return"), self, context=wdgctx).activated.connect(
- self.listAcceptToggle
- )
- QShortcut(QKeySequence("Ctrl+Enter"), self, context=wdgctx).activated.connect(
- self.listAcceptToggle
- )
- QShortcut(QKeySequence("Ctrl+Space"), self, context=wdgctx).activated.connect(
- self.listAcceptToggle
- )
+ QShortcut(QKeySequence(Qt.Key.Key_Enter), self, context=wdgctx).activated.connect(self.listAccept)
+ QShortcut(QKeySequence(Qt.Key.Key_Return), self, context=wdgctx).activated.connect(self.listAccept)
+ QShortcut(QKeySequence("Ctrl+Return"), self, context=wdgctx).activated.connect(self.listAcceptToggle)
+ QShortcut(QKeySequence("Ctrl+Enter"), self, context=wdgctx).activated.connect(self.listAcceptToggle)
+ QShortcut(QKeySequence("Ctrl+Space"), self, context=wdgctx).activated.connect(self.listAcceptToggle)
- QShortcut(
- QKeySequence(Qt.Key.Key_Escape), self, context=wdgctx
- ).activated.connect(self.listCancel)
+ QShortcut(QKeySequence(Qt.Key.Key_Escape), self, context=wdgctx).activated.connect(self.listCancel)
# Initialize the model with the full list (assuming the text() is empty)
# self.proxyFilterModel(self.text()) # This is done by refreshItemGroups on focusInEvent, because the initial loading from cache can take time
@@ -221,9 +238,7 @@ class SearchBox(QLineEdit):
[
QStandardItem(
genericToolIcon,
- translate(
- "SearchBar", "Please wait, loading results from cache…"
- ),
+ translate("SearchBar", "Please wait, loading results from cache…"),
),
QStandardItem("0"),
QStandardItem("-1"),
@@ -275,17 +290,11 @@ class SearchBox(QLineEdit):
@staticmethod
def proxyListPageDown(self):
- self.movementKey(
- lambda current, nbRows: min(
- current + max(1, self.maxVisibleRows / 2), nbRows - 1
- )
- )
+ self.movementKey(lambda current, nbRows: min(current + max(1, self.maxVisibleRows / 2), nbRows - 1))
@staticmethod
def proxyListPageUp(self):
- self.movementKey(
- lambda current, nbRows: max(current - max(1, self.maxVisibleRows / 2), 0)
- )
+ self.movementKey(lambda current, nbRows: max(current - max(1, self.maxVisibleRows / 2), 0))
@staticmethod
def proxyListEnd(self):
@@ -422,9 +431,7 @@ class SearchBox(QLineEdit):
def getScreenPosition(widget):
geo = widget.geometry()
parent = widget.parent()
- parentPos = (
- getScreenPosition(parent) if parent is not None else QPoint(0, 0)
- )
+ parentPos = getScreenPosition(parent) if parent is not None else QPoint(0, 0)
return QPoint(geo.x() + parentPos.x(), geo.y() + parentPos.y())
pos = getScreenPosition(self)
diff --git a/StyleMapping_SearchBar.py b/StyleMapping_SearchBar.py
new file mode 100644
index 0000000..4417b71
--- /dev/null
+++ b/StyleMapping_SearchBar.py
@@ -0,0 +1,239 @@
+# *************************************************************************
+# * *
+# * Copyright (c) 2019-2024 Paul Ebbers *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 3 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program 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 Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# *************************************************************************
+import FreeCAD as App
+import FreeCADGui as Gui
+import os
+from PySide.QtGui import QIcon, QPixmap, QAction
+from PySide.QtWidgets import (
+ QListWidgetItem,
+ QTableWidgetItem,
+ QListWidget,
+ QTableWidget,
+ QToolBar,
+ QToolButton,
+ QComboBox,
+ QPushButton,
+ QMenu,
+ QWidget,
+ QMainWindow,
+)
+from PySide.QtCore import Qt, SIGNAL, Signal, QObject, QThread
+import sys
+import json
+from datetime import datetime
+import shutil
+import Standard_Functions_RIbbon as StandardFunctions
+import Parameters_Ribbon
+import webbrowser
+import time
+
+# Get the resources
+pathIcons = Parameters_Ribbon.ICON_LOCATION
+pathStylSheets = Parameters_Ribbon.STYLESHEET_LOCATION
+pathUI = Parameters_Ribbon.UI_LOCATION
+pathBackup = Parameters_Ribbon.BACKUP_LOCATION
+sys.path.append(pathIcons)
+sys.path.append(pathStylSheets)
+sys.path.append(pathUI)
+sys.path.append(pathBackup)
+
+
+def ReturnStyleItem(ControlName, ShowCustomIcon=False, IgnoreOverlay=False):
+ """
+ Enter one of the names below:
+
+ ControlName (string):
+ "Background_Color" returns string,
+ "Border_Color" returns string,
+ "FontColor" returns string,
+ "FontColor" returns string,
+ """
+ # define a result holder and a dict for the StyleMapping file
+ result = "none"
+
+ # Get the current stylesheet for FreeCAD
+ FreeCAD_preferences = App.ParamGet("User parameter:BaseApp/Preferences/MainWindow")
+ currentStyleSheet = FreeCAD_preferences.GetString("StyleSheet")
+ IsInList = False
+ for key, value in StyleMapping_default["Stylesheets"].items():
+ if key == currentStyleSheet:
+ IsInList = True
+ break
+ if IsInList is False:
+ currentStyleSheet = "none"
+
+ try:
+ result = StyleMapping_default["Stylesheets"][currentStyleSheet][ControlName]
+ if result == "" or result is None:
+ result = StyleMapping_default["Stylesheets"][""][ControlName]
+ return result
+ except Exception as e:
+ print(e)
+ return None
+
+
+def ReturnColor(ColorType="Background_Color"):
+ mw: QMainWindow = Gui.getMainWindow()
+ palette = mw.style().standardPalette()
+ # Get the color
+ Color = palette.base().color().toTuple() # RGBA tupple
+ if ColorType == "Border_Color":
+ Color = palette.buttonText().color().toTuple()
+ if ColorType == "Background_Color_Hover":
+ Color = palette.highlight().color().toTuple()
+
+ HexColor = StandardFunctions.ColorConvertor(Color, Color[3] / 255, True, False)
+
+ return HexColor
+
+
+def ReturnFontColor():
+ fontColor = "#000000"
+ IsDarkTheme = DarkMode()
+
+ if IsDarkTheme is True:
+ fontColor = "#ffffff"
+
+ return fontColor
+
+
+def DarkMode():
+ import xml.etree.ElementTree as ET
+ import os
+
+ # Define the standard result
+ IsDarkTheme = False
+
+ # Get the current stylesheet for FreeCAD
+ FreeCAD_preferences = App.ParamGet("User parameter:BaseApp/Preferences/MainWindow")
+ currentStyleSheet = FreeCAD_preferences.GetString("StyleSheet")
+
+ path = os.path.dirname(__file__)
+ # Get the folder with add-ons
+ for i in range(2):
+ # Starting point
+ path = os.path.dirname(path)
+
+ # Go through the sub-folders
+ for root, dirs, files in os.walk(path):
+ for name in dirs:
+ # if the current stylesheet matches a sub directory, try to geth the pacakgexml
+ if currentStyleSheet.replace(".qss", "").lower() in name.lower():
+ try:
+ packageXML = os.path.join(path, name, "package.xml")
+
+ # Get the tree and root of the xml file
+ tree = ET.parse(packageXML)
+ treeRoot = tree.getroot()
+
+ # Get all the tag elements
+ elements = []
+ namespaces = {"i": "https://wiki.freecad.org/Package_Metadata"}
+ elements = treeRoot.findall(".//i:content/i:preferencepack/i:tag", namespaces)
+
+ # go throug all tags. If 'dark' in the element text, this is a dark theme
+ for element in elements:
+ if "dark" in element.text.lower():
+ IsDarkTheme = True
+ break
+ except Exception:
+ continue
+
+ return IsDarkTheme
+
+
+StyleMapping_default = {
+ "Stylesheets": {
+ "": {
+ "Background_Color": "#f0f0f0",
+ "Background_Color_Hover": "#ced4da",
+ "Border_Color": "#646464",
+ "FontColor": ReturnFontColor(),
+ },
+ "none": {
+ "Background_Color": "none",
+ "Background_Color_Hover": "#48a0f8",
+ "Border_Color": ReturnColor("Border_Color"),
+ "FontColor": ReturnFontColor(),
+ },
+ "FreeCAD Dark.qss": {
+ "Background_Color": "#333333",
+ "Background_Color_Hover": "#48a0f8",
+ "Border_Color": "#ffffff",
+ "FontColor": "#ffffff",
+ },
+ "FreeCAD Light.qss": {
+ "Background_Color": "#f0f0f0",
+ "Background_Color_Hover": "#48a0f8",
+ "Border_Color": "#646464",
+ "FontColor": "#000000",
+ },
+ "OpenLight.qss": {
+ "Background_Color": "#dee2e6",
+ "Background_Color_Hover": "#a5d8ff",
+ "Border_Color": "#1c7ed6",
+ "FontColor": "#000000",
+ },
+ "OpenDark.qss": {
+ "Background_Color": "#212529",
+ "Background_Color_Hover": "#1f364d",
+ "Border_Color": "#264b69",
+ "FontColor": "#ffffff",
+ },
+ "Behave-dark.qss": {
+ "Background_Color": "#232932",
+ "Background_Color_Hover": "#557bb6",
+ "Border_Color": "#3a7400",
+ "FontColor": ReturnFontColor(),
+ },
+ "ProDark.qss": {
+ "Background_Color": "#333333",
+ "Background_Color_Hover": "#557bb6",
+ "Border_Color": "#adc5ed",
+ "FontColor": ReturnFontColor(),
+ },
+ "Darker.qss": {
+ "Background_Color": "#444444",
+ "Background_Color_Hover": "#4aa5ff",
+ "Border_Color": "#696968",
+ "FontColor": ReturnFontColor(),
+ },
+ "Light-modern.qss": {
+ "Background_Color": "#f0f0f0",
+ "Background_Color_Hover": "#4aa5ff",
+ "Border_Color": "#646464",
+ "FontColor": ReturnFontColor(),
+ },
+ "Dark-modern.qss": {
+ "Background_Color": "#2b2b2b",
+ "Background_Color_Hover": "#4aa5ff",
+ "Border_Color": "#ffffff",
+ "FontColor": ReturnFontColor(),
+ },
+ "Dark-contrast.qss": {
+ "Background_Color": "#444444",
+ "Background_Color_Hover": "#4aa5ff",
+ "Border_Color": "#787878",
+ "FontColor": ReturnFontColor(),
+ },
+ }
+}
diff --git a/package.xml b/package.xml
index e7bf5ea..ff3b094 100644
--- a/package.xml
+++ b/package.xml
@@ -5,7 +5,7 @@
Adds a search bar widget for tools, document objects, and preferences
- 1.3.3
+ 1.4.0
2022-06-01
diff --git a/translations/update_translation.sh b/translations/update_translation.sh
index 1422655..77e7d36 100644
--- a/translations/update_translation.sh
+++ b/translations/update_translation.sh
@@ -114,8 +114,10 @@ help() {
# Main function ------------------------------------------------------------------------------------
+# LUPDATE="C:/Program Files/FreeCAD 1.0/bin/Lib/site-packages/PySide6/lupdate" # from Qt6
LUPDATE=/usr/lib/qt6/bin/lupdate # from Qt6
# LUPDATE=lupdate # from Qt5
+# LRELEASE="C:/Program Files/FreeCAD 1.0/bin/Lib/site-packages/PySide6/lrelease" # from Qt6
LRELEASE=/usr/lib/qt6/bin/lrelease # from Qt6
# LRELEASE=lrelease # from Qt5
WB="SearchBar"