FreeCAD

Форк
0
/
Macro.cpp 
398 строк · 11.0 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2004 Jürgen Riegel <juergen.riegel@web.de>              *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23

24
#include "PreCompiled.h"
25

26
#ifndef _PreComp_
27
# include <cassert>
28
# include <QFile>
29
# include <QTextStream>
30
#endif
31

32
#include <App/Application.h>
33
#include <Base/Console.h>
34
#include <Base/Exception.h>
35
#include <Base/Interpreter.h>
36

37
#include "Macro.h"
38
#include "MainWindow.h"
39
#include "PythonConsole.h"
40
#include "PythonConsolePy.h"
41
#include "PythonDebugger.h"
42

43

44
using namespace Gui;
45

46
MacroFile::MacroFile() = default;
47

48
void MacroFile::open(const char *sName)
49
{
50
    // check
51
#if _DEBUG
52
    Q_ASSERT(!this->openMacro);
53
#endif
54

55
    // Convert from Utf-8
56
    this->macroName = QString::fromUtf8(sName);
57
    if (!this->macroName.endsWith(QLatin1String(".FCMacro")))
58
        this->macroName += QLatin1String(".FCMacro");
59

60
    this->macroInProgress.clear();
61
    this->openMacro = true;
62
}
63

64
void MacroFile::append(const QString& line)
65
{
66
    this->macroInProgress.append(line);
67
}
68

69
void MacroFile::append(const QStringList& lines)
70
{
71
    this->macroInProgress.append(lines);
72
}
73

74
bool MacroFile::commit()
75
{
76
    QFile file(this->macroName);
77
    if (!file.open(QFile::WriteOnly)) {
78
        return false;
79
    }
80

81
    // sort import lines and avoid duplicates
82
    QTextStream str(&file);
83
    QStringList import;
84
    import << QString::fromLatin1("import FreeCAD");
85
    QStringList body;
86

87
    for (const auto& it : std::as_const(this->macroInProgress)) {
88
        if (it.startsWith(QLatin1String("import ")) ||
89
            it.startsWith(QLatin1String("#import "))) {
90
            if (import.indexOf(it) == -1)
91
                import.push_back(it);
92
        }
93
        else {
94
            body.push_back(it);
95
        }
96
    }
97

98
    QString header;
99
    header += QString::fromLatin1("# -*- coding: utf-8 -*-\n\n");
100
    header += QString::fromLatin1("# Macro Begin: ");
101
    header += this->macroName;
102
    header += QString::fromLatin1(" +++++++++++++++++++++++++++++++++++++++++++++++++\n");
103

104
    QString footer = QString::fromLatin1("# Macro End: ");
105
    footer += this->macroName;
106
    footer += QString::fromLatin1(" +++++++++++++++++++++++++++++++++++++++++++++++++\n");
107

108
    // write the data to the text file
109
    str << header;
110
    for (const auto& it : std::as_const(import)) {
111
        str << it << QLatin1Char('\n');
112
    }
113
    str << QLatin1Char('\n');
114
    for (const auto& it : body) {
115
        str << it << QLatin1Char('\n');
116
    }
117
    str << footer;
118

119
    this->macroInProgress.clear();
120
    this->macroName.clear();
121
    this->openMacro = false;
122
    file.close();
123
    return true;
124
}
125

126
void MacroFile::cancel()
127
{
128
    this->macroInProgress.clear();
129
    this->macroName.clear();
130
    this->openMacro = false;
131
}
132

133
// ----------------------------------------------------------------------------
134

135
MacroOutputBuffer::MacroOutputBuffer() = default;
136

137
void MacroOutputBuffer::addPendingLine(int type, const char* line)
138
{
139
    if (!line) {
140
        pendingLine.clear();
141
    }
142
    else {
143
        pendingLine.emplace_back(type, line);
144
    }
145
}
146

147
bool MacroOutputBuffer::addPendingLineIfComment(int type, const char* line)
148
{
149
    if (MacroOutputOption::isComment(type)) {
150
        pendingLine.emplace_back(type, line);
151
        return true;
152
    }
153

154
    return false;
155
}
156

157
void MacroOutputBuffer::incrementIfNoComment(int type)
158
{
159
    if (!MacroOutputOption::isComment(type)) {
160
        ++totalLines;
161
    }
162
}
163

164
// ----------------------------------------------------------------------------
165

166
MacroOutputOption::MacroOutputOption() = default;
167

168
std::tuple<bool, bool> MacroOutputOption::values(int type) const
169
{
170
    bool comment = isComment(type);
171
    bool record = true;
172

173
    if (isGuiCommand(type)) {
174
        if (recordGui && guiAsComment) {
175
            comment = true;
176
        }
177
        else if (!recordGui) {
178
            comment = true;
179
            record = false;
180
        }
181
    }
182

183
    return std::make_tuple(comment, record);
184
}
185

186
bool MacroOutputOption::isComment(int type)
187
{
188
    return type == MacroManager::Cmt;
189
}
190

191
bool MacroOutputOption::isGuiCommand(int type)
192
{
193
    return type == MacroManager::Gui;
194
}
195

196
bool MacroOutputOption::isAppCommand(int type)
197
{
198
    return type == MacroManager::App;
199
}
200

201
// ----------------------------------------------------------------------------
202

203
MacroManager::MacroManager()
204
  : pyDebugger(new PythonDebugger())
205
{
206
    // Attach to the Parametergroup regarding macros
207
    this->params = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro");
208
    this->params->Attach(this);
209
    this->params->NotifyAll();
210
}
211

212
MacroManager::~MacroManager()
213
{
214
    delete pyDebugger;
215
    this->params->Detach(this);
216
}
217

218
void MacroManager::OnChange(Base::Subject<const char*> &rCaller, const char * sReason)
219
{
220
    (void)rCaller;
221
    (void)sReason;
222
    option.recordGui         = this->params->GetBool("RecordGui", true);
223
    option.guiAsComment      = this->params->GetBool("GuiAsComment", true);
224
    option.scriptToPyConsole = this->params->GetBool("ScriptToPyConsole", true);
225
    this->localEnv           = this->params->GetBool("LocalEnvironment", true);
226
}
227

228
void MacroManager::open(MacroType eType, const char *sName)
229
{
230
    // check
231
#if _DEBUG
232
    assert(eType == File);
233
#else
234
    Q_UNUSED(eType);
235
#endif
236

237
    macroFile.open(sName);
238
    Base::Console().Log("CmdM: Open macro: %s\n", sName);
239
}
240

241
void MacroManager::commit()
242
{
243
    QString macroName = macroFile.fileName();
244
    if (macroFile.commit()) {
245
        Base::Console().Log("Commit macro: %s\n", (const char*)macroName.toUtf8());
246
    }
247
    else {
248
        Base::Console().Error("Cannot open file to write macro: %s\n",
249
            (const char*)macroName.toUtf8());
250
        cancel();
251
    }
252
}
253

254
void MacroManager::cancel()
255
{
256
    QString macroName = macroFile.fileName();
257
    Base::Console().Log("Cancel macro: %s\n",(const char*)macroName.toUtf8());
258
    macroFile.cancel();
259
}
260

261
void MacroManager::addPendingLine(LineType type, const char* line)
262
{
263
    buffer.addPendingLine(type, line);
264
}
265

266
void MacroManager::addLine(LineType Type, const char* sLine)
267
{
268
    if (!sLine)
269
        return;
270

271
    if (buffer.hasPendingLines()) {
272
        if (buffer.addPendingLineIfComment(Type, sLine)) {
273
            return;
274
        }
275

276
        processPendingLines();
277
    }
278

279
    buffer.incrementIfNoComment(Type);
280

281
    addToOutput(Type, sLine);
282
}
283

284
void MacroManager::processPendingLines()
285
{
286
    decltype(buffer.pendingLine) lines;
287
    lines.swap(buffer.pendingLine);
288
    for (auto &v : lines) {
289
        addLine(static_cast<LineType>(v.first), v.second.c_str());
290
    }
291
}
292

293
void MacroManager::makeComment(QStringList& lines) const
294
{
295
    for (auto &line : lines) {
296
        if (!line.startsWith(QLatin1String("#"))) {
297
            line.prepend(QLatin1String("# "));
298
        }
299
    }
300
}
301

302
void MacroManager::addToOutput(LineType type, const char* line)
303
{
304
    auto [comment, record] = option.values(type);
305

306
    QStringList lines = QString::fromUtf8(line).split(QLatin1String("\n"));
307
    if (comment) {
308
        makeComment(lines);
309
    }
310

311
    if (record && macroFile.isOpen()) {
312
        macroFile.append(lines);
313
    }
314

315
    if (option.scriptToPyConsole) {
316
        // search for the Python console
317
        auto console = getPythonConsole();
318
        if (console) {
319
            for(auto &line : lines) {
320
                console->printStatement(line);
321
            }
322
        }
323
    }
324
}
325

326
void MacroManager::setModule(const char* sModule)
327
{
328
    if (macroFile.isOpen() && sModule && *sModule != '\0')  {
329
        macroFile.append(QString::fromLatin1("import %1").arg(QString::fromLatin1(sModule)));
330
    }
331
}
332

333
PythonConsole* MacroManager::getPythonConsole() const
334
{
335
    // search for the Python console
336
    if (!this->pyConsole) {
337
        this->pyConsole = Gui::getMainWindow()->findChild<Gui::PythonConsole*>();
338
    }
339

340
    return this->pyConsole;
341
}
342

343
namespace Gui {
344
    class PythonRedirector
345
    {
346
    public:
347
        PythonRedirector(const char* type, PyObject* obj) : std_out(type), out(obj)
348
        {
349
            if (out) {
350
                Base::PyGILStateLocker lock;
351
                old = PySys_GetObject(std_out);
352
                PySys_SetObject(std_out, out);
353
            }
354
        }
355
        ~PythonRedirector()
356
        {
357
            if (out) {
358
                Base::PyGILStateLocker lock;
359
                PySys_SetObject(std_out, old);
360
                Py_DECREF(out);
361
            }
362
        }
363
    private:
364
        const char* std_out;
365
        PyObject* out;
366
        PyObject* old{nullptr};
367
    };
368
}
369

370
void MacroManager::run(MacroType eType, const char *sName)
371
{
372
    Q_UNUSED(eType);
373

374
    try {
375
        ParameterGrp::handle hGrp = App::GetApplication().GetUserParameter()
376
            .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("OutputWindow");
377
        PyObject* pyout = hGrp->GetBool("RedirectPythonOutput",true) ? new OutputStdout : nullptr;
378
        PyObject* pyerr = hGrp->GetBool("RedirectPythonErrors",true) ? new OutputStderr : nullptr;
379
        PythonRedirector std_out("stdout",pyout);
380
        PythonRedirector std_err("stderr",pyerr);
381
        //The given path name is expected to be Utf-8
382
        Base::Interpreter().runFile(sName, this->localEnv);
383
    }
384
    catch (const Base::SystemExitException&) {
385
        throw;
386
    }
387
    catch (const Base::PyException& e) {
388
        e.ReportException();
389
    }
390
    catch (const Base::Exception& e) {
391
        qWarning("%s",e.what());
392
    }
393
}
394

395
PythonDebugger* MacroManager::debugger() const
396
{
397
    return pyDebugger;
398
}
399

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

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

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

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