[Solved] How to select a point on Sketcher's screen for use in a macro?
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Be nice to others! Respect the FreeCAD code of conduct!
- ifohancroft
- Posts: 205
- Joined: Fri May 31, 2019 11:25 pm
- Location: Sofia, Bulgaria
- Contact:
[Solved] How to select a point on Sketcher's screen for use in a macro?
I have made a macro that creates a shape from lines and constraints it, however, it creates it on a fixed point on the screen.
I am trying to make the macro look and feel as close as possible to Sketcher's own geometry tools and for this there are a couple of things I want to do that I don't know how, but I am tackling them one at a time, so the first one is:
In a macro, after it gets executed, how do I wait for a for left mouse button click on the screen and get that click's X and Y coordinates?
I am trying to make the macro look and feel as close as possible to Sketcher's own geometry tools and for this there are a couple of things I want to do that I don't know how, but I am tackling them one at a time, so the first one is:
In a macro, after it gets executed, how do I wait for a for left mouse button click on the screen and get that click's X and Y coordinates?
Last edited by ifohancroft on Mon Sep 27, 2021 1:57 pm, edited 2 times in total.
I like making, breaking and modding stuff, using a soldering iron, code or both. https://ifohancroft.com
Re: How to select a point on Sketcher's screen for use in a macro?
You can use a ViewObserver filtering SoMouseEvent types. See example here : https://github.com/0penBrain/FreeCAD-ma ... og.FCMacro
- ifohancroft
- Posts: 205
- Joined: Fri May 31, 2019 11:25 pm
- Location: Sofia, Bulgaria
- Contact:
Re: How to select a point on Sketcher's screen for use in a macro?
Thank you!openBrain wrote: ↑Fri Sep 24, 2021 4:10 pm You can use a ViewObserver filtering SoMouseEvent types. See example here : https://github.com/0penBrain/FreeCAD-ma ... og.FCMacro
Now that I know about ViewObserver, I was able to find this: https://wiki.freecadweb.org/Code_snippe ... ick_action and used the example under 'Other example with ViewObserver on a object select or view' as a base, so I made this:
Code: Select all
import FreeCAD
import FreeCADGui
import Sketcher
activeView = Gui.activeDocument().ActiveView
class ViewObserver:
def __init__(self, view):
self.view = view
def logPosition(self, info):
isLeftMouseButton = (info["Button"] == "BUTTON1")
isClicked = (info["State"] == "DOWN")
clickPosition = info["Position"]
if (isLeftMouseButton and isClicked):
documentPosition = self.view.getPoint(clickPosition)
FreeCAD.Console.PrintMessage(str(documentPosition) + "\n---------------------------\n")
return documentPosition
viewObserver = ViewObserver(activeView)
callback = activeView.addEventCallback("SoMouseButtonEvent",viewObserver.logPosition)
FreeCAD.Console.PrintMessage(str(callback))
Also, couple of things that came up while I was writing this:
1. What is the general consensus on a way to structure code and limit it to the specific workbench? I mean like making sure my macro doesn't produce errors if executed while not in a sketch or without having a document? Is that even being done/needed for macros?
2. How do I make it wait until a left mouse button is pressed so the execution doesn't just continue if I have pressed the right button for example? Do I just make a loop inside of logPosition that keeps on looping until it receives a left click?
I like making, breaking and modding stuff, using a soldering iron, code or both. https://ifohancroft.com
Re: How to select a point on Sketcher's screen for use in a macro?
Does your macro have a GUI?
To me the best solution for you is to set the ViewObserver callback then wait it is called with a left click button. When it happens, just remove the callback and end your macro.
And yes, IMO should ensure a minimum of checks on the objects to ensure they are of the right type.
Moreover you have a special case with an observer callback. In this case, I would always place code in a try/catch and remove the observer if something goes wrong.
To me the best solution for you is to set the ViewObserver callback then wait it is called with a left click button. When it happens, just remove the callback and end your macro.
And yes, IMO should ensure a minimum of checks on the objects to ensure they are of the right type.
Moreover you have a special case with an observer callback. In this case, I would always place code in a try/catch and remove the observer if something goes wrong.
- ifohancroft
- Posts: 205
- Joined: Fri May 31, 2019 11:25 pm
- Location: Sofia, Bulgaria
- Contact:
Re: How to select a point on Sketcher's screen for use in a macro?
No. It does not. I execute it from the Macros window. I used to have an icon for it in the toolbar, but that's about it. I am just trying to add click coordinates for them so I can choose where to place the sketches inside of a Sketcher document.
How do I get the click coordinates into a variable outside of the ViewObserver?
Also, am I going about it the wrong way? Instead of trying to get the coordinates into a variable outside of the observer, then draw my sketch using those coordinates, am I supposed to make a class that draws my sketch, making that class be a callback for the active view's SoMouseButtonEvent and get the coordinates inside it?
Makes sense. Thank you!
Does it have to be like this? I did it this way because I don't know another and I thought I was supposed to do it this way?
I like making, breaking and modding stuff, using a soldering iron, code or both. https://ifohancroft.com
- ifohancroft
- Posts: 205
- Joined: Fri May 31, 2019 11:25 pm
- Location: Sofia, Bulgaria
- Contact:
Re: How to select a point on Sketcher's screen for use in a macro?
I think I got it.
It seems my confusion was coming from not understanding the concept well enough.
I think I managed to wrap my head around it.
In a bit I'll post what I came up with.
It seems my confusion was coming from not understanding the concept well enough.
I think I managed to wrap my head around it.
In a bit I'll post what I came up with.
I like making, breaking and modding stuff, using a soldering iron, code or both. https://ifohancroft.com
Re: How to select a point on Sketcher's screen for use in a macro?
In this case you have to inform the user the macro will 'pause' waiting for a click. Another cool way is to change the mouse pointer when waiting the position click, then restore it.ifohancroft wrote: ↑Fri Sep 24, 2021 6:05 pm No. It does not. I execute it from the Macros window. I used to have an icon for it in the toolbar, but that's about it. I am just trying to add click coordinates for them so I can choose where to place the sketches inside of a Sketcher document.
You can look at the example code I pointed out above. You can use a callback or a signal/slot. Actually there aren't really solution with single thread to really 'pause' the macro execution as it will also pause the event loop, hence freeze FreeCAD.How do I get the click coordinates into a variable outside of the ViewObserver?
I would have did it this way (I think). Instantiate a class that draws your sketch and declare one of its member function as being the ViewObserver callback. When it's called with correct arguments, remove the callback, finish the job and quit.Also, am I going about it the wrong way? Instead of trying to get the coordinates into a variable outside of the observer, then draw my sketch using those coordinates, am I supposed to make a class that draws my sketch, making that class be a callback for the active view's SoMouseButtonEvent and get the coordinates inside it?
The problem with the code above is that you never remove the callback, so it will stay active and each time you'll run the macro, a new one will be created, and all existing will be called when clicking.Does it have to be like this? I did it this way because I don't know another and I thought I was supposed to do it this way?
-
- Veteran
- Posts: 3112
- Joined: Thu Sep 24, 2020 10:31 pm
- Location: Hawaii
- Contact:
Re: How to select a point on Sketcher's screen for use in a macro?
https://github.com/edwilliams16/FreeCAD ... armacro.py is some code I wrote that takes two clicks and a number, n, to draw an n-pointed star shaped object in sketcher. You might find it has some useful code.
- ifohancroft
- Posts: 205
- Joined: Fri May 31, 2019 11:25 pm
- Location: Sofia, Bulgaria
- Contact:
Re: How to select a point on Sketcher's screen for use in a macro?
Apparently it took me about 9 hours, but I am very happy with the result:
Sorry for the lack of comments or an actual drawing part. I will add those later.
I am attaching it as a zip file along with the icon it uses, in-case anyone finds it useful or wants to try it. Just unzip it in your $HOME/.FreeCAD/Macros/ folder.
There were however, a couple of unexpected things, as well as couple of improvements I would like to make. Hence the following questions:
1. The ActiveSketch object doesn't get deleted when the Sketch does. What way would you recommend to check for the presence of an active sketch before drawing geometry or adding constraints? If you try to add geometry to a deleted sketch, by using ActiveSketch, there are no errors, just nothing happens, but I'd still like to have a proper check in place.
2. My custom cursor is too big. Is it the SVG file I have used or do I have to specify the scale or something? I couldn't actually find where FreeCAD holds its UI icons, so I downloaded the SVG from the wiki, then modified it a bit.
3. I am trying to imitate Sketcher's own tools with this macro, so I've made it that once you start it, you can keep clicking in the document and it will keep "drawing", unless you hit the right mouse button. Hitting the right mouse button shows the context menu on screen. Is there a way to prevent the context screen from showing when hitting the right mouse button while the macro is working?
Also, I just discovered that hitting the right mouse button doesn't actually stop the macro. Am I removing the event callback the wrong way?
3.1. Sketcher's native tools also stop working when you select another geometry tool or constraint - Can I catch the selection of another tool, so I can stop my macro when that happens?
4. I doubt that's possible, but if I add an icon for my macro to Sketcher's toolbar (by going to Tools > Customize > Macros, selecting a pixmap, etc, then going to the Sketcher's toolbar, adding a new one, adding the icon to it, etc), is it possible to make the icon be grayed out and unclickable when I am not editing a Sketch? (I.e. the way the icons for Sketch's own tools work)?
P.S. Any tips for improving my macro further?
Code: Select all
import FreeCAD
import FreeCADGui
import Sketcher
import PySide2
from PySide2 import QtGui, QtWidgets
from PySide2.QtWidgets import QApplication
from PySide2.QtGui import QPixmap, QCursor
class SwitchCutout:
def __init__(self):
self.changeCursor()
self.attachViewObserver()
def attachViewObserver(self):
if Gui.activeView() and repr(Gui.activeView()) == "View3DInventor":
self.view = Gui.activeView()
self.view_callback = self.view.addEventCallback("SoMouseButtonEvent", self.viewObserver)
else:
FreeCAD.Console.PrintMessage("ViewObserver cannot be set, no active 3D view\n")
def detachViewObserver(self):
self.view.removeEventCallback("SoMouseButtonEvent", self.viewObserver)
self.resetCursor()
def viewObserver(self, info):
if info["State"] == "DOWN":
if info["Button"] == "BUTTON1":
self.origin_x = self.view.getPoint(info["Position"])[0]
self.origin_y = self.view.getPoint(info["Position"])[1]
self.draw()
elif info["Button"] == "BUTTON2":
self.detachViewObserver()
def changeCursor(self):
cursorImage = FreeCAD.getUserAppDataDir() + "Macro/Icons/Sketcher_Pointer_Create_Switch.svg"
pixmap = QPixmap(cursorImage)
cursor = QCursor(pixmap)
QApplication.setOverrideCursor(cursor)
def resetCursor(self):
QApplication.restoreOverrideCursor()
def draw(self):
# Just a placeholder for now. I will add the drawing part later.
FreeCAD.Console.PrintMessage("You clicked: " + str(self.origin_x) + "," + str(self.origin_y) + "\n")
SwitchCutout()
I am attaching it as a zip file along with the icon it uses, in-case anyone finds it useful or wants to try it. Just unzip it in your $HOME/.FreeCAD/Macros/ folder.
There were however, a couple of unexpected things, as well as couple of improvements I would like to make. Hence the following questions:
1. The ActiveSketch object doesn't get deleted when the Sketch does. What way would you recommend to check for the presence of an active sketch before drawing geometry or adding constraints? If you try to add geometry to a deleted sketch, by using ActiveSketch, there are no errors, just nothing happens, but I'd still like to have a proper check in place.
2. My custom cursor is too big. Is it the SVG file I have used or do I have to specify the scale or something? I couldn't actually find where FreeCAD holds its UI icons, so I downloaded the SVG from the wiki, then modified it a bit.
3. I am trying to imitate Sketcher's own tools with this macro, so I've made it that once you start it, you can keep clicking in the document and it will keep "drawing", unless you hit the right mouse button. Hitting the right mouse button shows the context menu on screen. Is there a way to prevent the context screen from showing when hitting the right mouse button while the macro is working?
Also, I just discovered that hitting the right mouse button doesn't actually stop the macro. Am I removing the event callback the wrong way?
3.1. Sketcher's native tools also stop working when you select another geometry tool or constraint - Can I catch the selection of another tool, so I can stop my macro when that happens?
4. I doubt that's possible, but if I add an icon for my macro to Sketcher's toolbar (by going to Tools > Customize > Macros, selecting a pixmap, etc, then going to the Sketcher's toolbar, adding a new one, adding the icon to it, etc), is it possible to make the icon be grayed out and unclickable when I am not editing a Sketch? (I.e. the way the icons for Sketch's own tools work)?
P.S. Any tips for improving my macro further?
- Attachments
-
- TestMacro.zip
- (1.89 KiB) Downloaded 38 times
I like making, breaking and modding stuff, using a soldering iron, code or both. https://ifohancroft.com
Re: How to select a point on Sketcher's screen for use in a macro?
I had similair issues when I wanted to do something in Sketcher, I get the feeling there aren't many hooks to python. I don't have time to test but the cursor uses different images depending on tool selection, I thought that might be a way to detect states. The cursor is always default at startup.3.1. Sketcher's native tools also stop working when you select another geometry tool or constraint - Can I catch the selection of another tool, so I can stop my macro when that happens?