Unwanted Gui.Selection.clearSelection() on Draft_Move

A forum dedicated to the Draft, Arch and BIM workbenches development.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
User avatar
Roy_043
Veteran
Posts: 8450
Joined: Thu Dec 27, 2018 12:28 pm

Unwanted Gui.Selection.clearSelection() on Draft_Move

Post by Roy_043 »

When I Draft_Move a Draft_Wire I see this in the Python console:

Code: Select all

>>> # Gui.Selection.addSelection('Unnamed','Wire')
>>> Gui.runCommand('Draft_Move',0)
>>> # Gui.Selection.clearSelection()
>>> Draft.move([FreeCAD.ActiveDocument.Wire], FreeCAD.Vector(14.0, 13.0, 0.0), copy=False)
>>> # Gui.Selection.addSelection('Unnamed','Wire')
>>> FreeCAD.ActiveDocument.recompute()
>>> # Gui.Selection.clearSelection()
Gui.Selection.addSelection('Unnamed','Wire') is the result of Draft.select:
https://github.com/FreeCAD/FreeCAD/blob ... ls.py#L517

But which code is responsible for Gui.Selection.clearSelection() (occurs 2x)?
Clearing the selection after Draft.select is undesirable.

My steps:
  1. Create a Draft_Wire.
  2. Click in the 3D view to deselect.
  3. Clear the Python console.
  4. Select the wire in the Tree view.
  5. Move the wire with Draft_Move.

Code: Select all

OS: Ubuntu 22.04.1 LTS (ubuntu:GNOME/ubuntu)
Word size of FreeCAD: 64-bit
Version: 0.21.0.31641 (Git)
Build type: Release
Branch: master
Hash: d28d63b87b60161419c6c0c532fbbfaed96926b8
Python 3.10.8, Qt 5.15.6, Coin 4.0.0, Vtk 9.1.0, OCC 7.6.3
Locale: English/United States (en_US)

Code: Select all

OS: Windows 8.1 Version 6.3 (Build 9600)
Word size of FreeCAD: 64-bit
Version: 0.21.0.31641 (Git)
Build type: Release
Branch: master
Hash: d28d63b87b60161419c6c0c532fbbfaed96926b8
Python 3.10.8, Qt 5.15.6, Coin 4.0.0, Vtk 9.1.0, OCC 7.6.3
Locale: Dutch/Netherlands (nl_NL)
Installed mods:
hko
Posts: 96
Joined: Thu Apr 23, 2020 10:44 pm

Re: Unwanted Gui.Selection.clearSelection() on Draft_Move

Post by hko »

Roy_043 wrote: Sat Jan 28, 2023 7:24 pm Gui.Selection.addSelection('Unnamed','Wire') is the result of Draft.select:
You mean the second addSelection? The first addSelection is presumably the result of the step 4 and that does not involve Draft.select as far as I can tell.

Roy_043 wrote: Sat Jan 28, 2023 7:24 pm But which code is responsible for Gui.Selection.clearSelection() (occurs 2x)?
It's the same "normal" selection code (see backtrace below) that works based on 3D view mouse events whether the Draft_Move is active or not. From your console log I can see that the first point you selected was outside the wire and in fact outside of any object. But if you had clicked over an edge of the wire (even if due to snapping the actual starting point would end up being elsewhere), in addition to clearSelection(), there would have been something like

Code: Select all

Gui.Selection.addSelection('Unnamed','Wire','Edge1',98.285,85.9994,0)
And it could be some other object. In the screenshot below I'm moving draft wire -triangle but starting point was over an edge of a rectangle, so the edge of that rectangle is selected.

And once you select the end point, the same thing happens again and you either end up with something selected or not depending on what is "under" the second point.

Code: Select all

#0  0x0000155553fbefa0 in Gui::SelectionSingleton::clearSelection(char const*, bool)@plt () at /usr/local/freecad/lib/libFreeCADGui.so
#1  0x000015555463c51f in Gui::NavigationStyle::processEvent(SoEvent const*) (this=0x55555c2a6830, ev=0x55555c2a3e30) at ../src/Gui/NavigationStyle.cpp:1498
#2  0x0000155554681559 in Gui::View3DInventorViewer::processSoEvent(SoEvent const*) (this=0x55555c28cba0, ev=0x55555c2a3e30) at ../src/Gui/View3DInventorViewer.cpp:2275
#3  0x0000155554617813 in SIM::Coin3D::Quarter::EventFilter::eventFilter(QObject*, QEvent*) (this=0x55555c2a46c0, obj=0x55555c28cba0, qevent=0x7fffffffc010) at ../src/Gui/Quarter/EventFilter.cpp:167
#4  0x00001555503dc51b in QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*, QEvent*) (event=<optimized out>, receiver=<optimized out>) at kernel/qcoreapplication.cpp:1214
#5  QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*, QEvent*) (receiver=receiver@entry=0x55555c28cba0, event=event@entry=0x7fffffffc010) at kernel/qcoreapplication.cpp:1203
#6  0x0000155550df6a55 in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=this@entry=0x5555556f27c0, receiver=receiver@entry=0x55555c28cba0, e=e@entry=0x7fffffffc010) at kernel/qapplication.cpp:3694
#7  0x0000155550e00343 in QApplication::notify(QObject*, QEvent*) (this=<optimized out>, receiver=0x55555c2a02c0, e=0x7fffffffc500) at kernel/qapplication.cpp:3160
#8  0x00001555541902c0 in Gui::GUIApplication::notify(QObject*, QEvent*) (this=0x7fffffffd110, receiver=0x55555c2a02c0, event=0x7fffffffc500) at ../src/Gui/GuiApplication.cpp:85
#9  0x00001555503dc80a in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x55555c2a02c0, event=0x7fffffffc500) at ../../include/QtCore/../../src/corelib/kernel/qobject.h:142
#10 0x0000155550dff457 in QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool, bool)
    (receiver=receiver@entry=0x55555c2a02c0, event=event@entry=0x7fffffffc500, alienWidget=alienWidget@entry=0x55555c2a02c0, nativeWidget=0x7fffffffd150, buttonDown=buttonDown@entry=0x1555513268d0 <qt_button_down>, lastMouseReceiver=..., spontaneous=true, onlyDispatchEnterLeave=false) at kernel/qapplication.cpp:2646
#11 0x0000155550e5535d in QWidgetWindow::handleMouseEvent(QMouseEvent*) (this=0x555557554e20, event=0x7fffffffcba0) at /usr/include/c++/9/bits/atomic_base.h:413
#12 0x0000155550e581ec in QWidgetWindow::event(QEvent*) (event=0x7fffffffcba0, this=0x555557554e20) at kernel/qwidgetwindow.cpp:289
#13 QWidgetWindow::event(QEvent*) (this=0x555557554e20, event=0x7fffffffcba0) at kernel/qwidgetwindow.cpp:232
#14 0x0000155550df6a66 in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=this@entry=0x5555556f27c0, receiver=receiver@entry=0x555557554e20, e=e@entry=0x7fffffffcba0) at kernel/qapplication.cpp:3700
#15 0x0000155550e000f0 in QApplication::notify(QObject*, QEvent*) (this=0x7fffffffd110, receiver=0x555557554e20, e=0x7fffffffcba0) at kernel/qapplication.cpp:3446
#16 0x00001555541902c0 in Gui::GUIApplication::notify(QObject*, QEvent*) (this=0x7fffffffd110, receiver=0x555557554e20, event=0x7fffffffcba0) at ../src/Gui/GuiApplication.cpp:85
#17 0x00001555503dc80a in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x555557554e20, event=0x7fffffffcba0) at ../../include/QtCore/../../src/corelib/kernel/qobject.h:142
#18 0x00001555507c77d3 in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) () at /lib/x86_64-linux-gnu/libQt5Gui.so.5
#19 0x00001555507c910b in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) () at /lib/x86_64-linux-gnu/libQt5Gui.so.5
#20 0x00001555507a335b in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /lib/x86_64-linux-gnu/libQt5Gui.so.5
#21 0x0000155549ded32e in  () at /lib/x86_64-linux-gnu/libQt5XcbQpa.so.5
#22 0x000015554e08817d in g_main_context_dispatch () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#23 0x000015554e088400 in  () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#24 0x000015554e0884a3 in g_main_context_iteration () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#25 0x0000155550434435 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (this=0x5555558093d0, flags=...) at kernel/qeventdispatcher_glib.cpp:422
#26 0x00001555503db3ab in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (this=this@entry=0x7fffffffcf40, flags=..., flags@entry=...) at ../../include/QtCore/../../src/corelib/global/qflags.h:140
#27 0x00001555503e3116 in QCoreApplication::exec() () at ../../include/QtCore/../../src/corelib/global/qflags.h:120
#28 0x000015555402ef6e in Gui::Application::runApplication() () at ../src/Gui/Application.cpp:2265
#29 0x0000555555566987 in main(int, char**) (argc=1, argv=0x7fffffffdda8) at ../src/Main/MainGui.cpp:270
Screenshot_20230129_205341.png
Screenshot_20230129_205341.png (319.1 KiB) Viewed 647 times
User avatar
Roy_043
Veteran
Posts: 8450
Joined: Thu Dec 27, 2018 12:28 pm

Re: Unwanted Gui.Selection.clearSelection() on Draft_Move

Post by Roy_043 »

Thanks for your answer. I'll have to study this more. It seems that the snap tracker is still active when the command finishes.

hko wrote: Sun Jan 29, 2023 7:06 pm The first addSelection is presumably the result of the step 4
Correct.
User avatar
Roy_043
Veteran
Posts: 8450
Joined: Thu Dec 27, 2018 12:28 pm

Re: Unwanted Gui.Selection.clearSelection() on Draft_Move

Post by Roy_043 »

My diagnosis seems to be correct. I have only looked at gui_move.py. By adding self.view.removeEventCallback("SoEvent", self.call) to the local finish function, instead of having the finish function in the parent class handle this, followed by a delay, the problem can be solved.

The delay must come before ghost.finalize() for some reason, and I could not get the delay to work in the finish function of the parent class.

With the modified gui_move.py file the moved objects are correctly reselected after Draft_Move.

Delay code by chennes.
See https://forum.freecadweb.org/viewtopic. ... 62#p656362

Code: Select all

    def finish(self, cont=False):
        """Terminate the operation.

        Parameters
        ----------
        cont: bool or None, optional
            Restart (continue) the command if `True`, or if `None` and
            `ui.continueMode` is `True`.
        """
        if self.call:
            try:
                self.view.removeEventCallback("SoEvent", self.call)
                # Delay, must happen before ghost.finalize. Maybe because
                # of the use of ToDo.delay there?
                # Source https://forum.freecadweb.org/viewtopic.php?p=656362#p656362
                from PySide import QtCore
                class DelayEnder:
                    def __init__(self):
                        self.delay_is_done = False
                    def stop(self):
                        self.delay_is_done = True
                ender = DelayEnder()
                timer = QtCore.QTimer()
                timer.timeout.connect(ender.stop)
                timer.setSingleShot(True)
                timer.start(100) # 100ms (50ms is too short) timer guarantees the loop below runs at least that long
                while not ender.delay_is_done:
                    QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents)
            except RuntimeError:
                # the view has been deleted already
                pass
            self.call = None
        for ghost in self.ghosts:
            ghost.finalize()
        if cont or (cont is None and self.ui and self.ui.continueMode):
            todo.ToDo.delayAfter(self.Activated, [])
        super(Move, self).finish()
Attachments
gui_move.py
(13.75 KiB) Downloaded 24 times
Post Reply