diff --git a/CHANGELOG.md b/CHANGELOG.md index 00e3a33b..617a38d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ CHANGELOG for FlatCAM beta ================================================= +23.04.2020 + +- fixed the Tcl Command Help to work as expected; made the text of the commands to be colored in Red color and bold +- added a 'Close' menu entry in the Tcl Shell context menu that will close (hide) the Tcl Shell Dock widget +- on launching the Tcl Shell the Edit line will take focus immediately +- in App.on_mouse_move_over_plot() method no longer will be done a setFocus() on every move, only when it is needed + 22.04.2020 - added a new feature, project auto-saving controlled from Edit -> Preferences -> General -> APP. Preferences -> Enable Auto Save checkbox diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 2b6e0cee..d366afa8 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2105,7 +2105,7 @@ class App(QtCore.QObject): self.ui.menuview_toggle_axis.triggered.connect(self.on_toggle_axis) self.ui.menuview_toggle_workspace.triggered.connect(self.on_workspace_toggle) - self.ui.menutoolshell.triggered.connect(self.on_toggle_shell) + self.ui.menutoolshell.triggered.connect(self.toggle_shell) self.ui.menuhelp_about.triggered.connect(self.on_about) self.ui.menuhelp_manual.triggered.connect(lambda: webbrowser.open(self.manual_url)) @@ -3326,7 +3326,7 @@ class App(QtCore.QObject): # re-add the TCL Shell action to the Tools menu and reconnect it to ist slot function self.ui.menutoolshell = self.ui.menutool.addAction(QtGui.QIcon(self.resource_location + '/shell16.png'), '&Command Line\tS') - self.ui.menutoolshell.triggered.connect(self.on_toggle_shell) + self.ui.menutoolshell.triggered.connect(self.toggle_shell) # third install all of them try: @@ -3377,7 +3377,7 @@ class App(QtCore.QObject): self.ui.jmp_btn.triggered.connect(self.on_jump_to) self.ui.locate_btn.triggered.connect(lambda: self.on_locate(obj=self.collection.get_active())) - self.ui.shell_btn.triggered.connect(self.on_toggle_shell) + self.ui.shell_btn.triggered.connect(self.toggle_shell) self.ui.new_script_btn.triggered.connect(self.on_filenewscript) self.ui.open_script_btn.triggered.connect(self.on_fileopenscript) self.ui.run_script_btn.triggered.connect(self.on_filerunscript) @@ -5173,7 +5173,7 @@ class App(QtCore.QObject): # When the main event loop is not started yet in which case the qApp.quit() will do nothing # we use the following command # sys.exit(0) - os._exit(0) # fix to work with Python 3.8 + os._exit(0) # fix to work with Python 3.8 def kill_app(self): # QtCore.QCoreApplication.quit() @@ -5274,34 +5274,6 @@ class App(QtCore.QObject): with open(config_file, 'w') as f: f.writelines(data) - def on_toggle_shell(self): - """ - Toggle shell: if is visible close it, if it is closed then open it - :return: None - """ - - self.report_usage("on_toggle_shell()") - - if self.ui.shell_dock.isVisible(): - self.ui.shell_dock.hide() - else: - self.ui.shell_dock.show() - - def on_toggle_shell_from_settings(self, state): - """ - Toggle shell: if is visible close it, if it is closed then open it - :return: None - """ - - self.report_usage("on_toggle_shell_from_settings()") - - if state is True: - if not self.ui.shell_dock.isVisible(): - self.ui.shell_dock.show() - else: - if self.ui.shell_dock.isVisible(): - self.ui.shell_dock.hide() - def on_register_files(self, obj_type=None): """ Called whenever there is a need to register file extensions with FlatCAM. @@ -8601,8 +8573,11 @@ class App(QtCore.QObject): pan_button = 2 self.event_is_dragging = self.plotcanvas.is_dragging - # So it can receive key presses - self.plotcanvas.native.setFocus() + # So it can receive key presses but not when the Tcl Shell is active + if not self.ui.shell_dock.isVisible(): + if not self.plotcanvas.native.hasFocus(): + self.plotcanvas.native.setFocus() + self.pos_jump = event_pos self.ui.popMenu.mouse_is_panning = False @@ -12813,6 +12788,49 @@ class App(QtCore.QObject): """ pass + def toggle_shell(self): + """ + Toggle shell: if is visible close it, if it is closed then open it + :return: None + """ + + self.report_usage("toggle_shell()") + + if self.ui.shell_dock.isVisible(): + self.ui.shell_dock.hide() + self.plotcanvas.native.setFocus() + else: + self.ui.shell_dock.show() + + # I want to take the focus and give it to the Tcl Shell when the Tcl Shell is run + # self.shell._edit.setFocus() + QtCore.QTimer.singleShot(0, lambda: self.ui.shell_dock.widget()._edit.setFocus()) + + # HACK - simulate a mouse click - alternative + # no_km = QtCore.Qt.KeyboardModifier(QtCore.Qt.NoModifier) # no KB modifier + # pos = QtCore.QPoint((self.shell._edit.width() - 40), (self.shell._edit.height() - 2)) + # e = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, + # no_km) + # QtWidgets.qApp.sendEvent(self.shell._edit, e) + # f = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonRelease, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, + # no_km) + # QtWidgets.qApp.sendEvent(self.shell._edit, f) + + def on_toggle_shell_from_settings(self, state): + """ + Toggle shell: if is visible close it, if it is closed then open it + :return: None + """ + + self.report_usage("on_toggle_shell_from_settings()") + + if state is True: + if not self.ui.shell_dock.isVisible(): + self.ui.shell_dock.show() + else: + if self.ui.shell_dock.isVisible(): + self.ui.shell_dock.hide() + def shell_message(self, msg, show=False, error=False, warning=False, success=False, selected=False): """ Shows a message on the FlatCAM Shell diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index ded1d7c2..6f1fe86e 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -3247,7 +3247,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # Shell toggle if key == QtCore.Qt.Key_S: - self.app.on_toggle_shell() + self.app.toggle_shell() # Add a Tool from shortcut if key == QtCore.Qt.Key_T: diff --git a/flatcamGUI/GUIElements.py b/flatcamGUI/GUIElements.py index 94fa88e2..0b243456 100644 --- a/flatcamGUI/GUIElements.py +++ b/flatcamGUI/GUIElements.py @@ -2560,10 +2560,11 @@ class DialogBoxRadio(QtWidgets.QDialog): class _BrowserTextEdit(QTextEdit): - def __init__(self, version): + def __init__(self, version, app=None): QTextEdit.__init__(self) self.menu = None self.version = version + self.app = app def contextMenuEvent(self, event): self.menu = self.createStandardContextMenu(event.pos()) @@ -2571,6 +2572,12 @@ class _BrowserTextEdit(QTextEdit): clear_action.setShortcut(QKeySequence(Qt.Key_Delete)) # it's not working, the shortcut self.menu.addAction(clear_action) clear_action.triggered.connect(self.clear) + + if self.app: + close_action = QAction("Close", self) + self.menu.addAction(close_action) + close_action.triggered.connect(lambda: self.app.ui.shell_dock.hide()) + self.menu.exec_(event.globalPos()) def clear(self): diff --git a/flatcamTools/ToolShell.py b/flatcamTools/ToolShell.py index 78f328d5..0f8a5f0f 100644 --- a/flatcamTools/ToolShell.py +++ b/flatcamTools/ToolShell.py @@ -33,10 +33,10 @@ class TermWidget(QWidget): User pressed Enter. Client class should decide, if command must be executed or user may continue edit it """ - def __init__(self, version, *args): + def __init__(self, version, app, *args): QWidget.__init__(self, *args) - self._browser = _BrowserTextEdit(version=version) + self._browser = _BrowserTextEdit(version=version, app=app) self._browser.setStyleSheet("font: 9pt \"Courier\";") self._browser.setReadOnly(True) self._browser.document().setDefaultStyleSheet( @@ -92,10 +92,14 @@ class TermWidget(QWidget): """ Convert text to HTML for inserting it to browser """ - assert style in ('in', 'out', 'err', 'warning', 'success', 'selected') + assert style in ('in', 'out', 'err', 'warning', 'success', 'selected', 'raw') - text = html.escape(text) - text = text.replace('\n', '
') + if style != 'raw': + text = html.escape(text) + text = text.replace('\n', '
') + else: + text = text.replace('\n', '
') + text = text.replace('\t', '        ') if style == 'in': text = '%s' % text @@ -107,6 +111,8 @@ class TermWidget(QWidget): text = '%s' % text elif style == 'selected': text = '' + elif style == 'raw': + text = text else: text = '%s' % text # without span
is ignored!!! @@ -177,23 +183,29 @@ class TermWidget(QWidget): """ self._append_to_browser('out', text) + def append_raw(self, text): + """ + Append text to output widget as it is + """ + self._append_to_browser('raw', text) + def append_success(self, text): - """Appent text to output widget + """Append text to output widget """ self._append_to_browser('success', text) def append_selected(self, text): - """Appent text to output widget + """Append text to output widget """ self._append_to_browser('selected', text) def append_warning(self, text): - """Appent text to output widget + """Append text to output widget """ self._append_to_browser('warning', text) def append_error(self, text): - """Appent error text to output widget. Text is drawn with red background + """Append error text to output widget. Text is drawn with red background """ self._append_to_browser('err', text) @@ -235,7 +247,7 @@ class FCShell(TermWidget): :param version: FlatCAM version string :param args: Parameters passed to the TermWidget parent class """ - TermWidget.__init__(self, version, *args) + TermWidget.__init__(self, version, *args, app=sysShell) self._sysShell = sysShell def is_command_complete(self, text): diff --git a/tclCommands/TclCommandHelp.py b/tclCommands/TclCommandHelp.py index a1af436e..d40ac267 100644 --- a/tclCommands/TclCommandHelp.py +++ b/tclCommands/TclCommandHelp.py @@ -22,10 +22,10 @@ if '_' not in builtins.__dict__: class TclCommandHelp(TclCommand): """ - Tcl shell command to get the value of a system variable + Tcl shell command to show Help example: - get_sys excellon_zeros + help add_circle """ # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) @@ -58,21 +58,21 @@ class TclCommandHelp(TclCommand): def execute(self, args, unnamed_args): """ - :param args: + :param args: Without any argument will display the list of commands. Can have as a argument + a tcl command name to display the help for that command. :param unnamed_args: :return: """ - print(self.app.tcl_commands_storage) if 'name' in args: name = args['name'] if name not in self.app.tcl_commands_storage: return "Unknown command: %s" % name - print(self.app.tcl_commands_storage[name]["help"]) + self.app.shell.append_output(self.app.tcl_commands_storage[name]["help"]) else: - if args is None: - cmd_enum = _("Available commands:\n") + if not args: + cmd_enum = '%s\n' % _("Available commands:") displayed_text = [] try: @@ -85,14 +85,12 @@ class TclCommandHelp(TclCommand): max_tabs = math.ceil(max_len / 8) for cmd_name in sorted(self.app.tcl_commands_storage): - cmd_description = self.app.tcl_commands_storage[cmd_name]['description'] + cmd_description = "%s" % self.app.tcl_commands_storage[cmd_name]['description'] curr_len = len(cmd_name) tabs = '\t' - cmd_name_colored = "" - cmd_name_colored += str(cmd_name) - cmd_name_colored += "" + cmd_name_colored = "%s" % str(cmd_name) # make sure to add the right number of tabs (1 tab = 8 spaces) so all the commands # descriptions are aligned @@ -102,7 +100,7 @@ class TclCommandHelp(TclCommand): nr_tabs = 0 for x in range(max_tabs): - if curr_len <= (x * 8): + if curr_len < (x * 8): nr_tabs += 1 # nr_tabs = 2 if curr_len <= 8 else 1 @@ -111,9 +109,9 @@ class TclCommandHelp(TclCommand): displayed_text.append(cmd_line_txt) except Exception as err: self.app.log.debug("App.setup_shell.shelp() when run as 'help' --> %s" % str(err)) - displayed_text = [' %s' % cmd for cmd in sorted(self.app.tcl_commands_storage)] + displayed_text = ['> %s\n' % cmd for cmd in sorted(self.app.tcl_commands_storage)] - cmd_enum += '\n'.join(displayed_text) - cmd_enum += '\n\n%s\n%s' % (_("Type help for usage."), _("Example: help open_gerber")) + cmd_enum += '
'.join(displayed_text) + cmd_enum += '

%s
%s' % (_("Type help for usage."), _("Example: help open_gerber")) - print(cmd_enum) \ No newline at end of file + self.app.shell.append_raw(cmd_enum) diff --git a/tclCommands/__init__.py b/tclCommands/__init__.py index 9a5b22cf..b4a8d324 100644 --- a/tclCommands/__init__.py +++ b/tclCommands/__init__.py @@ -1,7 +1,6 @@ import pkgutil import sys -# Todo: I think these imports are not needed. # allowed command modules (please append them alphabetically ordered) import tclCommands.TclCommandAddCircle import tclCommands.TclCommandAddPolygon @@ -27,6 +26,7 @@ import tclCommands.TclCommandGeoCutout import tclCommands.TclCommandGeoUnion import tclCommands.TclCommandGetNames import tclCommands.TclCommandGetSys +import tclCommands.TclCommandHelp import tclCommands.TclCommandImportSvg import tclCommands.TclCommandInteriors import tclCommands.TclCommandIsolate @@ -93,7 +93,6 @@ def register_all_commands(app, commands): tcl_modules = {k: v for k, v in list(sys.modules.items()) if k.startswith('tclCommands.TclCommand')} for key, mod in list(tcl_modules.items()): - print(key) if key != 'tclCommands.TclCommand': class_name = key.split('.')[1] class_type = getattr(mod, class_name)