Macro Snip/ru

Макрос Snip (Обрезать)

Описание
Используйте этот макрос для удобства размещения скриншотов на форуме FreeCAD.
Лучше всего добавить этот макрос на вашу глобальную панель инструментов пользовательских макросов для быстрого и легкого доступа.
При публикации на форуме FreeCAD часто бывает полезно иметь возможность включать скриншоты. Проблема в том, что это несколько утомительная задача. Этот макрос призван немного упростить эту задачу.
Макрос может делать скриншоты или использовать существующие скриншоты, которые уже были скопированы в системный буфер обмена. Чтобы не использовать изображение, уже находящееся в буфере обмена, зажмите клавишу Shift во время вызова макроса. Чтобы использовать макрос для создания скриншота, настройте размер и расположение всплывающего диалогового окна, а затем щёлкните OK. Если щёлкнуть OK, макрос попытается сделать скриншот области экрана, которую занимает диалог. Сам диалог будет полупрозрачным, поэтому вы сможете увидеть его содержимое.

Макрос версии : 1.3
Дата последней модификации : 2024.07.23
FreeCAD версия : All
Скачать : ToolBar Icon
автор : TheMarkster
Автор
TheMarkster
Скачать
ToolBar Icon
Ссылки
Версия
1.3
Дата последней модификации
2024.07.23
Версии FreeCAD
All
Сочетание клавиш
На Windows: Windows Key + Shift + S

На Mac: Command + Shift + 4
На Linux: gnome-screenshot utility

Смотрите также
Макрос Copy3DViewToClipboard, Макрос Screen Wiki (Снимок для Вики)

Описание

Используйте этот макрос, чтобы легко размещать скриншоты на Форуме FreeCAD.

Лучше всего добавить этот макрос на глобальную панель инструментов пользовательских макросов для быстрого и легкого доступа.

При публикации на форуме FreeCAD часто бывает полезно иметь возможность включать скриншоты. Проблема в том, что это несколько утомительная задача. Поэтому этот макрос призван немного упростить эту задачу.

Применение

Макрос может делать скриншоты, а может использовать существующие скриншоты, которые уже были скопированы в системный буфер обмена. Чтобы не использовать изображение, уже находящееся в буфере обмена, нажмите клавишу Shift при вызове макроса. Чтобы использовать макрос для создания скриншота, настройте размер и расположение всплывающего диалога, а затем щёлкните OK. Если щёлкнуть OK, макрос попытается сделать скриншот области экрана, покрытой диалоговым окном. Сам диалог полупрозрачен, поэтому вы можете видеть его содержимое.

Обратите внимание, что диалоговое окно полупрозрачно. Будет отображено только содержимое экрана под диалогом.

После того как вы щёлкнете OK, макрос сделает снимок экрана и сохранит его во временном файле. Затем открывается диалог открытия файла. Из него вы можете перетащить файл на форум в текстовую область, в которой вы набираете текст для своего сообщения. После отмены диалога временный файл скриншота автоматически удаляется. Вы также можете открыть файл скриншота в программе, установленной по умолчанию для открытия файлов .png (в Windows это, как правило, Paint). Это может быть полезно, если вы хотите добавить к снимку экрана какие-то примечания или, возможно, дополнительно отредактировать его, например, обрезать.

Это диалог открытия файла, который появляется автоматически после создания скриншота. Изображение можно перетащить на форум или открыть его для дальнейшей обработки в системном приложении по умолчанию для открытия png-файлов. Кроме того, вы можете щёлкнуть правой кнопкой мыши по изображению и Open with... открыть с помощью другого приложения по вашему выбору.

Если макрос не работает в вашей системе для создания скриншотов, он все равно может быть полезен для скриншотов, которые вы сделали с помощью других инструментов. Просто скопируйте скриншот в буфер обмена, а затем запустите макрос. Он создаст временный файл и откроет его каталог в диалоге открытия файла. Некоторые другие инструменты для создания скриншотов:

Windows: Windows Key + Shift + S
MacOS: Command + Shift + 4
Linux: gnome-screenshot utility

Параметры

Макрос поддерживает пользовательские параметры, которые можно задать с помощью Инструменты → Редактор параметров... → Plugins → Snip_Macro

LastX, LastY, LastWidth, LastHeight: местоположение и размер поля для захвата при последнем использовании
WindowOpacity (0.85): значение между 0,0 (непрозрачный) и 1,0 (прозрачный)
SnipDelay (0.5): время (в секундах) задержки между закрытием окна снимка и самим снимком
DesiredWidth (0): желаемая ширина (в пикселях), игнорируется, если 0 -- масштабирует изображение до желаемого размера с сохранением текущего соотношения сторон
ScaleFactor (1.0): желаемый масштабный коэффициент, (переопределяет DesiredWidth, если ScaleFactor не 1.0) -- масштабирует изображение в соответствии с масштабным коэффициентом

Last (Последние) параметры сбрасываются макросом при каждом запуске. Так он отслеживает, куда поместить поле для снимка - в последнее место, где оно находилось, когда пользователь делал скриншот.

WindowOpacity параметр - это непрозрачность рамки для снимка.

SnipDelay параметр можно настроить, чтобы немного ускорить работу, но если значение слишком мало, то сделанный скриншот может включать само окно снимка, поскольку перед тем, как сделать скриншот, должно пройти время, чтобы закрыть диалог.

DesiredWidth параметр масштабирует изображение до желаемой ширины, если только он не равен 0 (по умолчанию), в этом случае масштабирование не производится. Например, если задать желаемую ширину 800, то вы получите изображения шириной 800 пикселей. Высота будет автоматически масштабироваться для сохранения текущего соотношения сторон. Если исходное изображение было 1600x1200, а значение DesiredWidth равно 800, то результирующее изображение будет 800x600.

ScaleFactor параметр масштабирует изображение до нужного соотношения, например, 0,5. По умолчанию используется значение 1.0, в этом случае масштабирование не производится. Этот параметр переопределяет параметр DesiredWidth, если ScaleFactor не равен 1.0.

Вы можете обойти любое масштабирование, удерживая нажатой клавишу Ctrl и нажимая кнопку Ok, чтобы сделать снимок экрана.

Скрипт

Значок на панели инструментов

Macro_Snip.FCMacro

# -*- coding: utf-8 -*-
"""
***************************************************************************
*   Copyright (c) 2019 <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__ = ""
__Wiki__ = ""
__date__ = "2024.07.23"
__version__ = 1.3
import FreeCAD
from PySide import QtGui,QtCore
import uuid
import time
import tempfile, os, shutil

# class SnipBox(QtGui.QDialog):
#     def __init__(self, scale, desired_width):
#         QtGui.QDialog.__init__(self)
#         self.scale = scale
#         self.desired_width = desired_width
#
#         self.detailsTextEdit = QtGui.QTextEdit("Details")
#         self.Details = QtGui.QPushButton("Details")
#         self.Scaling = QtGui.QPushButton("Scaling")
#         self.Details.clicked.connect(self.onDetailsClicked)
#         self.Scaling.clicked.connect(self.onScalingClicked)
#         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.addButton(self.Scaling, QtGui.QDialogButtonBox.ActionRole)
#         buttons.setCenterButtons(True)
#         layout.addWidget(buttons)
#         self.setLayout(layout)
#         self.detailsTextEdit.setVisible(False)
#
#     def setDetailedText(self, txt):
#         self.detailsTextEdit.setText(txt)
#
#     def setTitle(self, rectwidth, rectheight):
#         if self.scale != 1.0:
#            out_width = rect.width()*scale_factor
#         elif self.desired_width != 0:
#             out_width = desired_width
#         self.setWindowTitle(f"Snip macro v{__version__} {rectwidth} x {rectheight} output width {out_width}")
#
#
#     def onDetailsClicked(self):
#         self.detailsTextEdit.setVisible(not self.detailsTextEdit.isVisible())
#
#     def onScalingClicked(self):
#         pass
#
#     def event(self, e): #credit serge_gubenko of stackoverflow for this
#         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)
# #thanks to mario52 for this
#         if result == True:
#             try:
#                 rect = mb.frameGeometry()
#                 rectwidth  = rect.width()
#                 rectheight = rect.height()
#                 mb.setTitle(rectwidth,rectheight)
#
#             except Exception:
#                 None
# ##############
#         return result
class SnipBox(QtGui.QDialog):
    def __init__(self, scale, desired_width):
        QtGui.QDialog.__init__(self)
        self.pg = FreeCAD.ParamGet("User parameter:Plugins/Snip_Macro")
        self.scale = scale
        self.desired_width = desired_width

        self.detailsTextEdit = QtGui.QTextEdit("Details")
        self.Details = QtGui.QPushButton("Details")
        self.Scaling = QtGui.QPushButton("Scaling")
        self.Details.clicked.connect(self.onDetailsClicked)
        self.Scaling.clicked.connect(self.onScalingClicked)

        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.detailsTextEdit)
        layout.addStretch()

        # Scaling widgets
        self.scaleLabel = QtGui.QLabel("Scale:")
        self.scaleSpinBox = QtGui.QDoubleSpinBox()
        self.scaleSpinBox.setDecimals(2)
        self.scaleSpinBox.setSingleStep(0.1)
        self.scaleSpinBox.setValue(self.scale)
        self.scaleSpinBox.valueChanged.connect(self.updateScale)

        self.fixedWidthLabel = QtGui.QLabel("Fixed Width:")
        self.fixedWidthSpinBox = QtGui.QSpinBox()
        self.fixedWidthSpinBox.setRange(0, 10000)
        self.fixedWidthSpinBox.setValue(self.desired_width)
        self.fixedWidthSpinBox.valueChanged.connect(self.updateDesiredWidth)

        self.scaleLayout = QtGui.QVBoxLayout()
        self.scaleText = QtGui.QLabel("Scale values other than 1.0 will override fixed width setting")
        self.scaleLayout.addWidget(self.scaleText)
        scaleHBox1 = QtGui.QHBoxLayout()
        scaleHBox1.addWidget(self.scaleLabel)
        scaleHBox1.addWidget(self.scaleSpinBox)

        scaleHBox2 = QtGui.QHBoxLayout()
        scaleHBox2.addWidget(self.fixedWidthLabel)
        scaleHBox2.addWidget(self.fixedWidthSpinBox)

        self.scaleLayout.addLayout(scaleHBox1)
        self.scaleLayout.addLayout(scaleHBox2)

        self.scaleWidget = QtGui.QWidget()
        self.scaleWidget.setLayout(self.scaleLayout)
        self.scaleWidget.setVisible(False)

        layout.addWidget(self.scaleWidget)

        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.addButton(self.Scaling, QtGui.QDialogButtonBox.ActionRole)
        buttons.setCenterButtons(True)
        layout.addWidget(buttons)
        self.setLayout(layout)
        self.detailsTextEdit.setVisible(False)

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

    def setTitle(self, rectwidth, rectheight):
        if self.scale != 1.0:
            out_width = rectwidth * self.scale
        elif self.desired_width != 0:
            out_width = self.desired_width
        self.setWindowTitle(f"Snip macro v{__version__} {rectwidth} x {rectheight} output width {out_width}")

    def onDetailsClicked(self):
        if self.scaleWidget.isVisible():
            self.scaleWidget.setVisible(False)
        self.detailsTextEdit.setVisible(not self.detailsTextEdit.isVisible())

    def onScalingClicked(self):
        if self.detailsTextEdit.isVisible():
            self.detailsTextEdit.setVisible(False)
        self.scaleWidget.setVisible(not self.scaleWidget.isVisible())

    def updateScale(self, value):
        self.scale = value
        self.pg.SetFloat("ScaleFactor", self.scale)

    def updateDesiredWidth(self, value):
        self.desired_width = value
        self.pg.SetInt("DesiredWidth", self.desired_width)

    def event(self, e): #credit serge_gubenko of stackoverflow for this
        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)
#thanks to mario52 for this
        if result == True:
            try:
                rect = mb.frameGeometry()
                rectwidth  = rect.width()
                rectheight = rect.height()
                mb.setTitle(rectwidth,rectheight)
            except Exception:
                None
##############
        return result

def get_screen_for_widget(widget):
    screen_geometry = widget.geometry()
    for screen in QtGui.QApplication.screens():
        if screen.geometry().contains(screen_geometry.center()):
            return screen
def get_widget_screen_coordinates(widget):
    screen = get_screen_for_widget(widget)
    widget_geometry = widget.geometry()
    screen_geometry = screen.geometry()
    return QtCore.QRect(widget_geometry.x() - screen_geometry.x(),
                        widget_geometry.y() - screen_geometry.y(),
                        widget_geometry.width(),
                        widget_geometry.height())


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(scale_factor, desired_width)
    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()
        scale_factor = mb.scale
        desired_width = mb.desired_width
        rect = get_widget_screen_coordinates(mb)
        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()
            screen = get_screen_for_widget(mb)
            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.smoothScaled(rect.width()*scale_factor, rect.height()*scale_factor)
            elif desired_width != 0:
                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)

Ссылки

Обсуждение на форуме Snip macro