FreeCAD

Форк
0
/
PythonTracing.cpp 
198 строк · 5.4 Кб
1
// SPDX-License-Identifier: LGPL-2.1-or-later
2

3
/***************************************************************************
4
 *   Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net>     *
5
 *                                                                         *
6
 *   This file is part of FreeCAD.                                         *
7
 *                                                                         *
8
 *   FreeCAD is free software: you can redistribute it and/or modify it    *
9
 *   under the terms of the GNU Lesser General Public License as           *
10
 *   published by the Free Software Foundation, either version 2.1 of the  *
11
 *   License, or (at your option) any later version.                       *
12
 *                                                                         *
13
 *   FreeCAD is distributed in the hope that it will be useful, but        *
14
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
15
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
16
 *   Lesser General Public License for more details.                       *
17
 *                                                                         *
18
 *   You should have received a copy of the GNU Lesser General Public      *
19
 *   License along with FreeCAD. If not, see                               *
20
 *   <https://www.gnu.org/licenses/>.                                      *
21
 *                                                                         *
22
 **************************************************************************/
23

24
#include "PreCompiled.h"
25
#ifndef _PreComp_
26
#include <QApplication>
27
#include <QKeyEvent>
28
#include <QTime>
29
#include <QGuiApplication>
30
#endif
31

32
#include "PythonTracing.h"
33
#include <App/Application.h>
34
#include <Base/Interpreter.h>
35

36
using namespace Gui;
37

38
struct PythonTracing::Private
39
{
40
    bool active{false};
41
    int timeout{200}; //NOLINT
42

43
    // NOLINTBEGIN
44
    static int profilerInterval;
45
    static bool profilerDisabled;
46
    // NOLINTEND
47
};
48

49
// NOLINTBEGIN
50
int PythonTracing::Private::profilerInterval = 200;
51
bool PythonTracing::Private::profilerDisabled = false;
52
// NOLINTEND
53

54
PythonTracing::PythonTracing()
55
    : d{std::make_unique<Private>()}
56
{
57
}
58

59
PythonTracing::~PythonTracing() = default;
60

61
bool PythonTracing::isActive() const
62
{
63
    return d->active;
64
}
65

66
void PythonTracing::activate()
67
{
68
    d->active = true;
69
    setPythonTraceEnabled(true);
70
}
71

72
void PythonTracing::deactivate()
73
{
74
    d->active = false;
75
    setPythonTraceEnabled(false);
76
}
77

78
void PythonTracing::fetchFromSettings()
79
{
80
    const long defaultTimeout = 200;
81

82
    auto parameterGroup = App::GetApplication().GetParameterGroupByPath(
83
        "User parameter:BaseApp/Preferences/PythonConsole");
84
    int interval = static_cast<int>(parameterGroup->GetInt("ProfilerInterval", defaultTimeout));
85
    setTimeout(interval);
86
}
87

88
bool PythonTracing::interrupt() const
89
{
90
    if (isActive()) {
91
        PyErr_SetInterrupt();
92
        return true;
93
    }
94

95
    return false;
96
}
97

98
void PythonTracing::setTimeout(int ms)
99
{
100
    d->timeout = ms;
101
}
102

103
int PythonTracing::timeout() const
104
{
105
    return d->timeout;
106
}
107

108
void PythonTracing::setPythonTraceEnabled(bool enabled) const
109
{
110
    Py_tracefunc trace = nullptr;
111
    if (enabled && timeout() > 0) {
112
        Private::profilerInterval = timeout();
113
        trace = &tracer_callback;
114
    }
115
    else {
116
        Private::profilerDisabled = true;
117
    }
118

119
    Base::PyGILStateLocker lock;
120
    PyEval_SetTrace(trace, nullptr);
121
}
122

123
/*
124
 * This callback ensures that Qt runs its event loop (i.e. updates the GUI, processes keyboard and
125
 * mouse events, etc.) at least every 200 ms, even when there is long-running Python code on the
126
 * main thread. It is registered as the global trace function of the Python environment.
127
 */
128
int PythonTracing::tracer_callback(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg)
129
{
130
    Q_UNUSED(obj)
131
    Q_UNUSED(frame)
132
    Q_UNUSED(what)
133
    Q_UNUSED(arg)
134

135
    static QTime lastCalledTime = QTime::currentTime();
136
    QTime currTime = QTime::currentTime();
137

138
    // if previous code object was executed
139
    if (Private::profilerDisabled) {
140
        Private::profilerDisabled = false;
141
        lastCalledTime = currTime;
142
    }
143

144
    int ms = lastCalledTime.msecsTo(currTime);
145

146
    if (ms >= Private::profilerInterval) {
147
        lastCalledTime = currTime;
148
        QGuiApplication::processEvents();
149
    }
150

151
    return 0;
152
}
153

154
// ------------------------------------------------------------------------------------------------
155

156
PythonTracingLocker::PythonTracingLocker(PythonTracing& trace)
157
    : trace{trace}
158
{
159
    trace.activate();
160
}
161

162
PythonTracingLocker::~PythonTracingLocker()
163
{
164
    trace.deactivate();
165
}
166

167
// ------------------------------------------------------------------------------------------------
168

169
PythonTracingWatcher::PythonTracingWatcher(QObject* parent)
170
    : QObject(parent)
171
{
172
    qApp->installEventFilter(this);
173
}
174

175
PythonTracingWatcher::~PythonTracingWatcher()
176
{
177
    qApp->removeEventFilter(this);
178
}
179

180
bool PythonTracingWatcher::eventFilter(QObject* object, QEvent* event)
181
{
182
    if (event && event->type() == QEvent::ShortcutOverride) {
183
        auto kevent = static_cast<QKeyEvent*>(event);
184
        if (kevent->key() == Qt::Key_C && kevent->modifiers() == Qt::ControlModifier) {
185
            if (trace.interrupt()) {
186
                return true;
187
            }
188
        }
189
    }
190

191
    return QObject::eventFilter(object, event);
192
}
193

194
PythonTracing& PythonTracingWatcher::getTrace()
195
{
196
    trace.fetchFromSettings();
197
    return trace;
198
}
199

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.