FreeCAD
295 строк · 12.6 Кб
1# SPDX-License-Identifier: LGPL-2.1-or-later
2# ***************************************************************************
3# * *
4# * Copyright (c) 2022-2024 The FreeCAD Project Association AISBL *
5# * *
6# * This file is part of FreeCAD. *
7# * *
8# * FreeCAD is free software: you can redistribute it and/or modify it *
9# * under the terms of the GNU Lesser General Public License as *
10# * published by the Free Software Foundation, either version 2.1 of the *
11# * License, or (at your option) any later version. *
12# * *
13# * FreeCAD is distributed in the hope that it will be useful, but *
14# * WITHOUT ANY WARRANTY; without even the implied warranty of *
15# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16# * Lesser General Public License for more details. *
17# * *
18# * You should have received a copy of the GNU Lesser General Public *
19# * License along with FreeCAD. If not, see *
20# * <https://www.gnu.org/licenses/>. *
21# * *
22# ***************************************************************************
23
24""" A collection of functions to handle installing a macro icon to the toolbar. """
25
26import os
27
28import FreeCAD
29import FreeCADGui
30from PySide import QtCore, QtWidgets
31import Addon
32
33translate = FreeCAD.Qt.translate
34
35
36def ask_to_install_toolbar_button(repo: Addon) -> None:
37"""Presents a dialog to the user asking if they want to install a toolbar button for
38a particular macro, and walks through that process if they agree to do so."""
39pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
40do_not_show_dialog = pref.GetBool("dontShowAddMacroButtonDialog", False)
41button_exists = check_for_button(repo)
42if not do_not_show_dialog and not button_exists:
43add_toolbar_button_dialog = FreeCADGui.PySideUic.loadUi(
44os.path.join(os.path.dirname(__file__), "add_toolbar_button_dialog.ui")
45)
46add_toolbar_button_dialog.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint, True)
47add_toolbar_button_dialog.buttonYes.clicked.connect(lambda: install_toolbar_button(repo))
48add_toolbar_button_dialog.buttonNever.clicked.connect(
49lambda: pref.SetBool("dontShowAddMacroButtonDialog", True)
50)
51add_toolbar_button_dialog.exec()
52
53
54def check_for_button(repo: Addon) -> bool:
55"""Returns True if a button already exists for this macro, or False if not."""
56command = FreeCADGui.Command.findCustomCommand(repo.macro.filename)
57if not command:
58return False
59custom_toolbars = FreeCAD.ParamGet("User parameter:BaseApp/Workbench/Global/Toolbar")
60toolbar_groups = custom_toolbars.GetGroups()
61for group in toolbar_groups:
62toolbar = custom_toolbars.GetGroup(group)
63if toolbar.GetString(command, "*") != "*":
64return True
65return False
66
67
68def ask_for_toolbar(repo: Addon, custom_toolbars) -> object:
69"""Determine what toolbar to add the icon to. The first time it is called it prompts the
70user to select or create a toolbar. After that, the prompt is optional and can be configured
71via a preference. Returns the pref group for the new toolbar."""
72pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
73
74# In this one spot, default True: if this is the first time we got to
75# this chunk of code, we are always going to ask.
76ask = pref.GetBool("alwaysAskForToolbar", True)
77
78if ask:
79select_toolbar_dialog = FreeCADGui.PySideUic.loadUi(
80os.path.join(os.path.dirname(__file__), "select_toolbar_dialog.ui")
81)
82select_toolbar_dialog.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint, True)
83
84select_toolbar_dialog.comboBox.clear()
85
86for group in custom_toolbars:
87ref = FreeCAD.ParamGet("User parameter:BaseApp/Workbench/Global/Toolbar/" + group)
88name = ref.GetString("Name", "")
89if name:
90select_toolbar_dialog.comboBox.addItem(name)
91else:
92FreeCAD.Console.PrintWarning(
93f"Custom toolbar {group} does not have a Name element\n"
94)
95new_menubar_option_text = translate("AddonsInstaller", "Create new toolbar")
96select_toolbar_dialog.comboBox.addItem(new_menubar_option_text)
97
98result = select_toolbar_dialog.exec()
99if result == QtWidgets.QDialog.Accepted:
100selection = select_toolbar_dialog.comboBox.currentText()
101if select_toolbar_dialog.checkBox.checkState() == QtCore.Qt.Unchecked:
102pref.SetBool("alwaysAskForToolbar", False)
103else:
104pref.SetBool("alwaysAskForToolbar", True)
105if selection == new_menubar_option_text:
106return create_new_custom_toolbar()
107return get_toolbar_with_name(selection)
108return None
109
110# If none of the above code returned...
111custom_toolbar_name = pref.GetString("CustomToolbarName", "Auto-Created Macro Toolbar")
112toolbar = get_toolbar_with_name(custom_toolbar_name)
113if not toolbar:
114# They told us not to ask, but then the toolbar got deleted... ask anyway!
115ask = pref.RemBool("alwaysAskForToolbar")
116return ask_for_toolbar(repo, custom_toolbars)
117return toolbar
118
119
120def get_toolbar_with_name(name: str) -> object:
121"""Try to find a toolbar with a given name. Returns the preference group for the toolbar
122if found, or None if it does not exist."""
123top_group = FreeCAD.ParamGet("User parameter:BaseApp/Workbench/Global/Toolbar")
124custom_toolbars = top_group.GetGroups()
125for toolbar in custom_toolbars:
126group = FreeCAD.ParamGet("User parameter:BaseApp/Workbench/Global/Toolbar/" + toolbar)
127group_name = group.GetString("Name", "")
128if group_name == name:
129return group
130return None
131
132
133def create_new_custom_toolbar() -> object:
134"""Create a new custom toolbar and returns its preference group."""
135
136# We need two names: the name of the auto-created toolbar, as it will be displayed to the
137# user in various menus, and the underlying name of the toolbar group. Both must be
138# unique.
139
140# First, the displayed name
141custom_toolbar_name = "Auto-Created Macro Toolbar"
142top_group = FreeCAD.ParamGet("User parameter:BaseApp/Workbench/Global/Toolbar")
143custom_toolbars = top_group.GetGroups()
144name_taken = check_for_toolbar(custom_toolbar_name)
145if name_taken:
146i = 2 # Don't use (1), start at (2)
147while True:
148test_name = custom_toolbar_name + f" ({i})"
149if not check_for_toolbar(test_name):
150custom_toolbar_name = test_name
151i = i + 1
152
153# Second, the toolbar preference group name
154i = 1
155while True:
156new_group_name = "Custom_" + str(i)
157if new_group_name not in custom_toolbars:
158break
159i = i + 1
160
161custom_toolbar = FreeCAD.ParamGet(
162"User parameter:BaseApp/Workbench/Global/Toolbar/" + new_group_name
163)
164custom_toolbar.SetString("Name", custom_toolbar_name)
165custom_toolbar.SetBool("Active", True)
166return custom_toolbar
167
168
169def check_for_toolbar(toolbar_name: str) -> bool:
170"""Returns True if the toolbar exists, otherwise False"""
171return get_toolbar_with_name(toolbar_name) is not None
172
173
174def install_toolbar_button(repo: Addon) -> None:
175"""If the user has requested a toolbar button be installed, this function is called
176to continue the process and request any additional required information."""
177pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
178custom_toolbar_name = pref.GetString("CustomToolbarName", "Auto-Created Macro Toolbar")
179
180# Default to false here: if the variable hasn't been set, we don't assume
181# that we have to ask, because the simplest is to just create a new toolbar
182# and never ask at all.
183ask = pref.GetBool("alwaysAskForToolbar", False)
184
185# See if there is already a custom toolbar for macros:
186top_group = FreeCAD.ParamGet("User parameter:BaseApp/Workbench/Global/Toolbar")
187custom_toolbars = top_group.GetGroups()
188if custom_toolbars:
189# If there are already custom toolbars, see if one of them is the one we used last time
190found_toolbar = False
191for toolbar_name in custom_toolbars:
192test_toolbar = FreeCAD.ParamGet(
193"User parameter:BaseApp/Workbench/Global/Toolbar/" + toolbar_name
194)
195name = test_toolbar.GetString("Name", "")
196if name == custom_toolbar_name:
197custom_toolbar = test_toolbar
198found_toolbar = True
199break
200if ask or not found_toolbar:
201# We have to ask the user what to do...
202custom_toolbar = ask_for_toolbar(repo, custom_toolbars)
203if custom_toolbar:
204custom_toolbar_name = custom_toolbar.GetString("Name")
205pref.SetString("CustomToolbarName", custom_toolbar_name)
206else:
207# Create a custom toolbar
208custom_toolbar = FreeCAD.ParamGet(
209"User parameter:BaseApp/Workbench/Global/Toolbar/Custom_1"
210)
211custom_toolbar.SetString("Name", custom_toolbar_name)
212custom_toolbar.SetBool("Active", True)
213
214if custom_toolbar:
215install_macro_to_toolbar(repo, custom_toolbar)
216else:
217FreeCAD.Console.PrintMessage("In the end, no custom toolbar was set, bailing out\n")
218
219
220def find_installed_icon(repo: Addon) -> str:
221"""The icon the macro specifies is usually not the actual installed icon, but rather a cached
222copy. This function looks for a file with the same name located in the macro installation
223path."""
224macro_repo_dir = FreeCAD.getUserMacroDir(True)
225if repo.macro.icon:
226basename = os.path.basename(repo.macro.icon)
227# Simple case first: the file is just in the macro directory...
228if os.path.isfile(os.path.join(macro_repo_dir, basename)):
229return os.path.join(macro_repo_dir, basename)
230# More complex: search for it
231for root, dirs, files in os.walk(macro_repo_dir):
232for name in files:
233if name == basename:
234return os.path.join(root, name)
235return ""
236elif repo.macro.xpm:
237return os.path.normpath(os.path.join(macro_repo_dir, repo.macro.name + "_icon.xpm"))
238else:
239return ""
240
241
242def install_macro_to_toolbar(repo: Addon, toolbar: object) -> None:
243"""Adds an icon for the given macro to the given toolbar."""
244menuText = repo.display_name
245tooltipText = f"<b>{repo.display_name}</b>"
246if repo.macro.comment:
247tooltipText += f"<br/><p>{repo.macro.comment}</p>"
248whatsThisText = repo.macro.comment
249else:
250whatsThisText = translate(
251"AddonsInstaller", "A macro installed with the FreeCAD Addon Manager"
252)
253statustipText = (
254translate("AddonsInstaller", "Run", "Indicates a macro that can be 'run'")
255+ " "
256+ repo.display_name
257)
258pixmapText = find_installed_icon(repo)
259
260# Add this command to that toolbar
261command_name = FreeCADGui.Command.createCustomCommand(
262repo.macro.filename,
263menuText,
264tooltipText,
265whatsThisText,
266statustipText,
267pixmapText,
268)
269toolbar.SetString(command_name, "FreeCAD")
270
271# Force the toolbars to be recreated
272wb = FreeCADGui.activeWorkbench()
273wb.reloadActive()
274
275
276def remove_custom_toolbar_button(repo: Addon) -> None:
277"""If this repo contains a macro, look through the custom commands and
278see if one is set up for this macro. If so, remove it, including any
279toolbar entries."""
280
281command = FreeCADGui.Command.findCustomCommand(repo.macro.filename)
282if not command:
283return
284custom_toolbars = FreeCAD.ParamGet("User parameter:BaseApp/Workbench/Global/Toolbar")
285toolbar_groups = custom_toolbars.GetGroups()
286for group in toolbar_groups:
287toolbar = custom_toolbars.GetGroup(group)
288if toolbar.GetString(command, "*") != "*":
289toolbar.RemString(command)
290
291FreeCADGui.Command.removeCustomCommand(command)
292
293# Force the toolbars to be recreated
294wb = FreeCADGui.activeWorkbench()
295wb.reloadActive()
296