Snip macro

Info about new community or project announcements, implemented features, classes, modules or APIs. Might get technical!
PLEASE DO NOT POST HELP REQUESTS OR OTHER DISCUSSIONS HERE!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
chrisb
Veteran
Posts: 53920
Joined: Tue Mar 17, 2015 9:14 am

Re: Snip macro

Post by chrisb »

Values for opacity of 0.5 to 0.6 are best for me and the presets for size and position work wonderful.

I am not quite happy with the place where the parameters are stored, but that may need a more general discussion.
The parameters are almost on top level in Preferences->Snip_Macro at the same level as other system parameters. I would expect them in some general subfolder. Candidates for that are the folder "Addon" - or "Plugins->addon_repository" which both contain directly some parameters.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Snip macro

Post by TheMarkster »

I think you are right. The parameters should not be in BaseApp at all, but rather in Plugins. I need to change that now before too many people install it.

Edit: Done

New version 1.06.

It checks if the parameters exist in BaseApp/Preferences and relocates them to Plugins.

Addon Manager should probably have a mechanism for cleaning up user parameters when uninstalling macros and workbenches. But for that there would need to be a standard place to put them, perhaps Plugins/macro_name would work. Then when the addon manager uninstalls a macro it could also remove any parameter group named Plugins/macro_name. The user could be queried first if a preference group is found. For my workbenches I have them in BaseApp/Preferences/Mod/workbench_name, but I think BaseApp should be reserved only for FreeCAD base and workbenches that come pre-installed.

Perhaps there should be Plugins/Macros and Plugins/Workbenches heirarchy. But I don't think many macros use parameters anyway.

Alternatively, each macro that creates user parameter groups could implement a parameter cleanup function that the Addon manager calls when uninstalling, maybe something like def onUninstall(): and inside there cleanup any parameter groups / folders / files created. But this would require cooperation from macro writers, which would be hit or miss. Better, I think, to just check for a Plugins/macro_name group and remove it if the user wants to when uninstalling. Sometimes it could be the user wants to reinstall, and so wants to keep the settings.

There is no real harm in having orphaned parameters from uninstalled macros/workbenches, but it probably has a performance cost the bigger the user.cfg file becomes. The clutter would also make it more difficult for users to find parameters.
chrisb
Veteran
Posts: 53920
Joined: Tue Mar 17, 2015 9:14 am

Re: Snip macro

Post by chrisb »

The old values are even preserved. Today I used the Snip macro for a real forum post!
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Snip macro

Post by TheMarkster »

Version 1.07

Can now work with python2/qt4 builds.
mario52
Veteran
Posts: 4673
Joined: Wed May 16, 2012 2:13 pm

Re: Snip macro

Post by mario52 »

hi TheMarkster

Proposition upgrade :
drmacro wrote:ping
1: display the resolution of the window

2: spinBox for multi monitor (Snip Macro)

snipNew00.png
snipNew00.png (11.44 KiB) Viewed 2253 times

the modification are into taged ####new
example:

Code: Select all

####new
#
global mb;
global numberSreen ; numberSreen = 0
#
####new
not tested with multi monitors !

the code modified :

Code: Select all


# -*- coding: utf-8 -*-
"""
***************************************************************************
*   Copyright (c) 2019 2020 2021<TheMarkster>                             *
*                                                                         *
*   This file is a supplement to the FreeCAD CAx development system.      *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU Lesser General Public License (LGPL)    *
*   as published by the Free Software Foundation; either version 2 of     *
*   the License, or (at your option) any later version.                   *
*                                                                         *
*   This software is distributed in the hope that it will be useful,      *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU Library General Public License at http://www.gnu.org/licenses     *
*   for more details.                                                     *
*                                                                         *
*   For more information about the GNU Library General Public License     *
*   write to the Free Software Foundation, Inc., 59 Temple Place,         *
*   Suite 330, Boston, MA  02111-1307 USA                                 *
*                                                                         *
***************************************************************************
"""



"""
Snip Macro

This is a macro to make it easier to post screenshots to the FreeCAD forum.

The forum supports a drag and drop interface -- just drag and drop an image to the
textarea where you type in your post.  But it still requires many
tedious steps.  This macro is to reduce some of those steps.

You should add the macro to your custom macro toolbar to save as many clicks as possible.

The first thing the macro does is to check if there is an image already saved to the 
system clipboard, and if so, it uses that one.  To bypass the clipboard, press Shift
while invoking the macro.

"""

__title__ = "Snip"
__author__ = "TheMarkster"
__url__ = "https://forum.freecadweb.org/viewtopic.php?f=9&t=38328&sid=385bf3174dcae7fb8bdf529f4e76dfed"
__Wiki__ = "https://wiki.freecadweb.org/Macro_Snip"
__date__ = "2021.03.21"
__version__ = 1.22
import FreeCAD
import PySide
from PySide import QtGui,QtCore
import uuid
import time
import tempfile, os, shutil

####new
#
global mb;
global numberSreen ; numberSreen = 0
#
####new

class SnipBox(QtGui.QDialog):
    def __init__(self):
        QtGui.QDialog.__init__(self)
        self.detailsTextEdit = QtGui.QTextEdit("Details")
        self.Details = QtGui.QPushButton("Details")
        self.Details.clicked.connect(self.onDetailsClicked)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.detailsTextEdit)
        layout.addStretch()
        buttons = QtGui.QDialogButtonBox(
            QtGui.QDialogButtonBox.Ok,
            QtCore.Qt.Horizontal, self)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        buttons.addButton(self.Details, QtGui.QDialogButtonBox.ActionRole)
        buttons.setCenterButtons(True)
        layout.addWidget(buttons)
####new
#
        screenS = QtGui.QSpinBox()
        screenS.valueChanged.connect(self.on_screenS) #connection SpinBox
        screenS.setPrefix("Monitor n : ")
        layout.addWidget(screenS)
#
####new
        self.setLayout(layout)
        self.detailsTextEdit.setVisible(False)
####new
#
    def on_screenS(self, value):
        global numberSreen
        numberSreen = value
#
####new

    def setDetailedText(self, txt):
        self.detailsTextEdit.setText(txt)

    def onDetailsClicked(self):
        self.detailsTextEdit.setVisible(not self.detailsTextEdit.isVisible())

    def event(self, e): #credit serge_gubenko of stackoverflow for this
        global mb
        result = QtGui.QDialog.event(self, e)
        self.setMinimumHeight(0)
        self.setMaximumHeight(16777215)
        self.setMinimumWidth(0)
        self.setMaximumWidth(16777215)
        self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        textEdit = self.findChild(QtGui.QTextEdit)
        if textEdit != None :
            textEdit.setMinimumHeight(0)
            textEdit.setMaximumHeight(16777215)
            textEdit.setMinimumWidth(0)
            textEdit.setMaximumWidth(16777215)
            textEdit.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
####new
#
        if result == True:
            try:
                rect = mb.frameGeometry()
                rectwidth  = rect.width()
                rectheight = rect.height()
                mb.setWindowTitle("Snip macro v"+str(__version__)+"  "+str(rectwidth)+" x "+str(rectheight))
            except Exception:
                None
#
####new
        return result

pg = FreeCAD.ParamGet("User parameter:Plugins/Snip_Macro")
#parameters were originally in BaseApp, but should be in Plugins, so relocate if necessary
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences").HasGroup("Snip_Macro"):
    deprecated = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Snip_Macro")
    pg.SetInt("LastX",deprecated.GetInt("LastX",0))
    pg.SetInt("LastY",deprecated.GetInt("LastY",0))
    pg.SetInt("LastWidth",deprecated.GetInt("LastWidth",0))
    pg.SetInt("LastHeight",deprecated.GetInt("LastHeight",0))
    pg.SetFloat("WindowOpacity",deprecated.GetFloat("WindowOpacity",0.85))
    pg.SetFloat("SnipDelay",deprecated.GetFloat("SnipDelay",0.5))
    FreeCAD.ParamGet("User parameter:BaseApp/Preferences").RemGroup("Snip_Macro")

desired_width = pg.GetInt("DesiredWidth",0) #used for scaling to desired width, maintaining aspect ratio
pg.SetInt("DesiredWidth", desired_width)
scale_factor = pg.GetFloat("ScaleFactor", 1.0) #scale by scale factor instead of desired width
pg.SetFloat("ScaleFactor", scale_factor)

skipClipboard = False
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ShiftModifier:
    skipClipboard = True

userCanceled = False
fname = "Snip macro screenshot-"+str(uuid.uuid4())[:6]+".png"
image = None
if not skipClipboard:
    clip = QtGui.QClipboard()
    image = clip.image(QtGui.QClipboard.Clipboard)
if not image:

    #use our own screen grabber
    mb = SnipBox()     
    details = """
Move and resize this box to cover the part of the screen you wish to grab.
If it succeeds an open file dialog will appear.  Drag and drop the file
from the open file dialog to the forum.  The file will be deleted after
you close the dialog.

If this fails you can still use this macro to handle screenshots you
copied to the clipboard using other tools:

Windows snip tool: Window Key + Shift + S
Mac snip tool: Command + Shift + 4
Linux: gnome-screenshot utility

If the macro finds there is already an image copied to the clipboard 
it uses that image instead of bringing up this dialog.  Press Shift
while invoking this macro to bypass the clipboard.  Alternatively,
you can clear the image from the clipboard by copying some text to it.

The Open button will open the file in the system
default application for handling png files, e.g. Paint in windows.  This
can be useful if you wish to annotate the screenshot.  But most of the
time you will simply want to drag and drop the file to the forum, then
Cancel to close the open file dialog afterwards.

If you wish to open the screenshot file with another application, right-click
the file and select open with... option or drag/drop to that other application.

User Parameters: These can be accessed via Tools menu -> Edit Parameters in 
Plugins -> Snip_Macro:

LastX, LastY, LastWidth, LastHeight -- location and size of snip box last use
WindowOpacity (0.85) -- value between 0.0 (less opaque) and 1.0 (more opaque)
SnipDelay (0.5) -- time (in seconds) delay between snip box close and snip
DesiredWidth -- image will be scaled to this width (unless it is 0) maintaining
current aspect ratio.
ScaleFactor -- float value, e.g. 0.5 -- image will be scaled to that scale factor.
Note: ScaleFactor (if not 1.0) will take precedence over DesiredWidth
Hold down Ctrl key to ignore scaling.

The SnipDelay parameter can be adjusted to speed things up a bit, but if it 
is too small the screenshot taken might include the snip box itself because
we need to wait for it to close before taking the screenshot.

"""
    mb.setWindowTitle("Snip macro v"+str(__version__))
    mb.setDetailedText(details)
    if pg.GetFloat("WindowOpacity",0.85) == 0.85:
        pg.SetFloat("WindowOpacity",0.85)
    mb.setWindowOpacity(pg.GetFloat("WindowOpacity",0.85))
    lastX = pg.GetInt("LastX",0)
    lastY = pg.GetInt("LastY",0)
    lastWidth = pg.GetInt("LastWidth",100)
    lastHeight = pg.GetInt("LastHeight",100)
    mb.setGeometry(lastX, lastY, lastWidth, lastHeight)
    mb.resize(lastWidth, lastHeight)
    mb.setWindowFlags(QtCore.Qt.Tool)
    result = mb.exec_()
    if not result:
        userCanceled = True
    if not userCanceled:
        clientRect = mb.geometry()
        rect = mb.frameGeometry()
        diff = rect.height()-clientRect.height()
        pg.SetInt("LastX", rect.x())
        pg.SetInt("LastY", rect.y())
        pg.SetInt("LastWidth", rect.width())
        pg.SetInt("LastHeight", rect.height()-diff)
        QtGui.QApplication.processEvents()
        snipDelay = pg.GetFloat("SnipDelay", 0.5)
        if snipDelay == 0.5:
            pg.SetFloat("SnipDelay", 0.5)
        time.sleep(snipDelay) #give time for dialog to close before taking screenshot
        QtGui.QApplication.processEvents()
        if hasattr(QtGui.QApplication,"primaryScreen"):
            #screen = QtGui.QApplication.primaryScreen() #original
####new
#
            try:
                screen = QtGui.QApplication.screens()[numberSreen]  #or [1]  or [2] depending on which display to use
            except Exception:
                screen = QtGui.QApplication.screens()[0]   # [0]  one display to use
                numberSreen = 0
#
####new
            image = screen.grabWindow(0,rect.x(),rect.y(),rect.width(),rect.height()).toImage()
        else:
            long_qdesktop_id = QtGui.QApplication.desktop().winId()
            image = QtGui.QPixmap.grabWindow(long_qdesktop_id, rect.x(), rect.y(), rect.width(), rect.height()).toImage()
        if not image:
            raise Exception("Snip Macro Error: Unable to grab screen image\n")

        modifiers = QtGui.QApplication.keyboardModifiers()
        skipScaling = False
        if modifiers == QtCore.Qt.ControlModifier:
            skipScaling = True
        if not skipScaling:
            if scale_factor != 1.0:
                #image = image.scaledToWidth(rect.width()*scale_factor)
                image = image.smoothScaled(rect.width()*scale_factor, rect.height()*scale_factor)
            elif desired_width != 0:
                #image = image.scaledToWidth(desired_width)
                image = image.smoothScaled(desired_width, rect.height()*float(desired_width)/float(rect.width()))

if not userCanceled:
    dirPath = tempfile.mkdtemp()
    filePath = dirPath + os.path.sep + fname
    image.save(filePath)
    fileName = QtGui.QFileDialog.getOpenFileName(QtGui.QApplication.activeWindow(),
    "Drag the image to the forum, then Cancel will delete the temporary file", dirPath, "PNG (*.png)")
    if fileName[0]:  #user selected Open or double-clicked file
        import subprocess, os, platform
        if platform.system() == 'Darwin':       # macOS
            subprocess.call(('open', fileName[0]))
        elif platform.system() == 'Windows':    # Windows
            os.startfile(fileName[0])
        else:                                   # linux variants
            subprocess.call(('xdg-open', fileName[0]))
        QtGui.QApplication.processEvents() #allow some time for file to open before deleting temp folder and contents
        time.sleep(1)
        QtGui.QApplication.processEvents()
    shutil.rmtree(dirPath)
    
    
mario
Maybe you need a special feature, go into Macros_recipes and Code_snippets, Topological_data_scripting.
My macros on Gist.github here complete macros Wiki and forum.
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Snip macro

Post by TheMarkster »

mario52 wrote: Fri Mar 26, 2021 6:42 pm hi TheMarkster

Proposition upgrade :

...
mario
Hi, mario. Thanks for your suggestion. I did not see this message before. You pinged drmacro instead of me. I have 2 monitors. The current version am I using works quite well no matter which monitor I drag the macro window to.

I tested your macro. I get a capture of screen 1 if I set to screen 1 even if the macro window is in screen 0. In other words I can put the macro over the middle of screen 0, set it to screen 0 and get a capture of screen 0. If I leave the macro where it is without moving it and set to screen 1 I get a capture of screen 1 even though the macro is still positioned on screen 0.

I think because the version I have already works well with 2 monitors on my setup I will not use this suggestion, but I will use the idea to put the rectangle size on the title bar. New version is now 1.22.
mario52
Veteran
Posts: 4673
Joined: Wed May 16, 2012 2:13 pm

Re: Snip macro

Post by mario52 »

Hi TheMarkster
TheMarkster wrote: Wed Feb 23, 2022 7:01 am You pinged drmacro instead of me
the upgrade for TheMarkster and the testing for drmacro related the Snip Macro post

if I understood correctly my upgrade works, good i have only one monitor and my upgrade is for my theoretical

i use the drmacro snippet, for this the ping

good day

mario
Maybe you need a special feature, go into Macros_recipes and Code_snippets, Topological_data_scripting.
My macros on Gist.github here complete macros Wiki and forum.
Post Reply