Procházet zdrojové kódy

- 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

Marius Stanciu před 5 roky
rodič
revize
3735753a93

+ 7 - 0
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

+ 52 - 34
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

+ 1 - 1
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:

+ 8 - 1
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):

+ 22 - 10
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', '<br/>')
+        if style != 'raw':
+            text = html.escape(text)
+            text = text.replace('\n', '<br/>')
+        else:
+            text = text.replace('\n', '<br>')
+            text = text.replace('\t', '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')
 
         if style == 'in':
             text = '<span style="font-weight: bold;">%s</span>' % text
@@ -107,6 +111,8 @@ class TermWidget(QWidget):
             text = '<span style="font-weight: bold; color: #15b300;">%s</span>' % text
         elif style == 'selected':
             text = ''
+        elif style == 'raw':
+            text = text
         else:
             text = '<span>%s</span>' % text  # without span <br/> 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):

+ 14 - 16
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 = "<span>%s</span>" % self.app.tcl_commands_storage[cmd_name]['description']
 
                         curr_len = len(cmd_name)
                         tabs = '\t'
 
-                        cmd_name_colored = "<span style=\" color:#ff0000;\" >"
-                        cmd_name_colored += str(cmd_name)
-                        cmd_name_colored += "</span>"
+                        cmd_name_colored = "<span style=\" font-weight: bold; color: red;\" >%s</span>" % 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 <command_name> for usage."), _("Example: help open_gerber"))
+                cmd_enum += '<br>'.join(displayed_text)
+                cmd_enum += '<br><br>%s<br>%s' % (_("Type help <command_name> for usage."), _("Example: help open_gerber"))
 
-                print(cmd_enum)
+                self.app.shell.append_raw(cmd_enum)

+ 1 - 2
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)