16 #include <QInputDialog>
17 #include <QApplication>
18 #include <QFileDialog>
23 #include "hal_config.h"
29 #if PY_VERSION_HEX < 0x030900a0
41 :
QObject(parent), mContext(nullptr), mSender(nullptr), mTriggerReset(false), mTriggerClear(false),
42 mThread(nullptr), mThreadAborted(false), mInterpreterCaller(nullptr)
44 py::initialize_interpreter();
59 mMainThreadState = PyEval_SaveThread();
65 PyEval_RestoreThread(mMainThreadState);
68 py::finalize_interpreter();
88 mThreadAborted =
true;
103 std::string command =
"import __main__\n"
104 "import io, sys, threading, builtins\n";
107 command +=
"sys.path.append('" + path.string() +
"')\n";
111 "from hal_gui.console import reset\n"
112 "from hal_gui.console import clear\n"
113 "class StdOutCatcher(io.TextIOBase):\n"
114 " def __init__(self):\n"
116 " def write(self, stuff):\n"
117 " from hal_gui import console\n"
118 " if threading.current_thread() is threading.main_thread():\n"
119 " console.redirector.write_stdout(stuff)\n"
121 " console.redirector.thread_stdout(stuff)\n"
122 "class StdErrCatcher(io.TextIOBase):\n"
123 " def __init__(self):\n"
125 " def write(self, stuff):\n"
126 " from hal_gui import console\n"
127 " if threading.current_thread() is threading.main_thread():\n"
128 " console.redirector.write_stderr(stuff)\n"
130 " console.redirector.thread_stderr(stuff)\n"
131 "def redirect_input(prompt=\"Please enter input:\"):\n"
132 " if threading.current_thread() is threading.main_thread():\n"
133 " return \"input in main thread not supported\"\n"
135 " return console.redirector.thread_stdin(prompt)\n"
136 "sys.stdout = StdOutCatcher()\n"
137 "sys.__stdout__ = sys.stdout\n"
138 "sys.stderr = StdErrCatcher()\n"
139 "sys.__stderr__ = sys.stderr\n"
140 "builtins.input = redirect_input\n"
141 "builtins.raw_input = redirect_input\n"
143 "import pydoc, pkgutil\n"
144 "def hal_listmodules(self, key=''):\n"
145 " names = set(sys.builtin_module_names) | {m.name for m in pkgutil.iter_modules()}\n"
147 " names = {n for n in names if key.lower() in n.lower()}\n"
148 " self.list(sorted(names))\n"
149 "pydoc.Helper.listmodules = hal_listmodules\n"
152 py::exec(command, *context, *context);
166 std::string command =
"import __main__\n"
167 "import io, sys, threading\n"
168 "from hal_gui.console import reset\n"
169 "from hal_gui.console import clear\n"
172 py::exec(command, *context, *context);
190 mContext =
new py::dict(**py::globals());
193 (*mContext)[
"console"] = py::module::import(
"hal_gui.console");
194 (*mContext)[
"hal_gui"] = py::module::import(
"hal_gui");
211 if (
input ==
"quit()")
213 forwardError(
"quit() cannot be used in this interpreter. Use console.reset() to restart it.\n");
219 forwardError(
"python thread already running. Please retry after other thread finished.\n");
222 log_info(
"python",
"Python console execute: \"{}\".",
input.toStdString());
224 mInterpreterCaller = caller;
225 startThread(
input,!multiple_expressions);
229 void PythonContext::startThread(
const QString& input,
bool singleStatement)
248 log_info(
"python",
"Python console execute: \"{}\".",
input.toStdString());
252 PyGILState_STATE
state = PyGILState_Ensure();
262 rc = py::eval<py::eval_single_statement>(
input.toStdString(), *mContext, *mContext);
269 catch (py::error_already_set& e)
271 qDebug() <<
"interpret error set";
276 catch (std::exception& e)
278 qDebug() <<
"interpret exception";
283 PyGILState_Release(
state);
292 log_warning(
"python",
"Not executed, python script already running");
298 log_info(
"UserStudy",
"Python editor execute script:\n{}\n",
input.toStdString());
304 mInterpreterCaller = caller;
305 startThread(
input,
false);
308 void PythonContext::handleScriptOutput(
const QString& txt)
314 void PythonContext::handleScriptError(
const QString& txt)
320 void PythonContext::handleInputRequired(
int type,
const QString& prompt,
const QVariant &defaultValue)
336 if (!confirm) userInput.
clear();
337 if (mThread) mThread->
setInput(userInput);
343 if (mThread) mThread->
setInput(userInput);
351 PythonGateSelectionReceiver* pgs =
new PythonGateSelectionReceiver(mThread,
this);
352 GateDialog gd(gats, prompt, pgs, qApp->activeWindow());
356 if (!gd.pickerModeActivated() && mThread) mThread->
setInput(QVariant::fromValue<void*>(gatSelect));
363 PythonModuleSelectionReceiver* pms =
new PythonModuleSelectionReceiver(mThread,
this);
364 ModuleDialog md({}, prompt,
false, pms, qApp->activeWindow());
368 if (!md.pickerModeActivated() && mThread) mThread->
setInput(QVariant::fromValue<void*>(modSelect));
374 if (mThread) mThread->
setInput(userInput);
387 if (mThread) mThread->
setInput(QVariant::fromValue<void*>(gatSelect));
396 if (mThread) mThread->
setInput(QVariant::fromValue<void*>(modSelect));
400 void PythonContext::handleConsoleInputReceived(
const QString& input)
407 void PythonContext::handleThreadFinished()
410 PythonEditor* calledFromEditor =
dynamic_cast<PythonEditor*
>(mInterpreterCaller);
414 mThreadAborted =
false;
428 mThreadAborted =
false;
433 delete mLayoutLocker;
438 if (calledFromEditor)
440 calledFromEditor->handleThreadFinished();
443 forwardError(
"\nreset() can only be used in the Python console!\n");
444 mTriggerReset =
false;
448 forwardError(
"\nclear() can only be used in the Python console!\n");
449 mTriggerClear =
false;
489 PyGILState_STATE
state = PyGILState_Ensure();
491 std::vector<std::tuple<std::string, std::string>> ret_val;
494 auto namespaces = py::list();
495 if (use_console_context)
497 namespaces.append(*mContext);
498 namespaces.append(*mContext);
502 py::dict tmp_context = py::globals();
504 namespaces.append(tmp_context);
505 namespaces.append(tmp_context);
507 auto jedi = py::module::import(
"jedi");
508 py::object script = jedi.attr(
"Interpreter")(text.
toStdString(), namespaces);
510 if (py::hasattr(script,
"complete"))
511 list = script.attr(
"complete")();
512 else if (py::hasattr(script,
"completions"))
513 list = script.attr(
"completions")();
515 log_warning(
"python",
"Jedi autocompletion failed, neither complete() nor completions() found.");
517 for (
const auto& entry : list)
519 auto a = entry.attr(
"name_with_symbols").cast<std::string>();
520 auto b = entry.attr(
"complete").cast<std::string>();
521 ret_val.emplace_back(a, b);
524 catch (py::error_already_set& e)
531 PyGILState_Release(
state);
540 PyGILState_STATE
state = PyGILState_Ensure();
545 py::module codeop = py::module::import(
"codeop");
546 py::object compiled = codeop.attr(
"compile_command")(text.
toStdString(),
"stdin",
"exec");
547 result = compiled.is_none() ? 0 : 1;
549 catch (py::error_already_set& e)
560 PyGILState_Release(
state);
564 void PythonContext::handleReset()
568 PyGILState_STATE
state = PyGILState_Ensure();
571 PyGILState_Release(state);
573 mTriggerReset =
false;
577 void PythonContext::handleClear()
585 mTriggerClear =
false;
591 mTriggerReset =
true;
596 mTriggerClear =
true;
601 PyGILState_STATE
state = PyGILState_Ensure();
603 PyGILState_Release(
state);
const std::vector< Gate * > & get_gates() const
Gate * get_gate_by_id(const u32 gate_id) const
Module * get_module_by_id(u32 module_id) const
An interactable python console.
void handleThreadFinished()
void setInputMode(bool state)
void inputReceived(QString input)
virtual void clear() override
virtual void handleStdout(const QString &output) override
virtual void handleError(const QString &output) override
std::vector< std::tuple< std::string, std::string > > complete(const QString &text, bool use_console_context)
PythonContext(QObject *parent=nullptr)
int checkCompleteStatement(const QString &text)
PythonThread * pythonThread() const
bool interpretBackground(QObject *caller, const QString &input, bool multiple_expressions=false)
static void initializeContext(py::dict *context)
void setConsole(PythonConsole *console)
void interpretScript(QObject *caller, const QString &input)
void forwardError(const QString &output)
void interpretForeground(const QString &input)
void abortThreadAndWait()
void forwardStdout(const QString &output)
static void initializeScript(py::dict *context)
void handleGatesPicked(const QSet< u32 > &gats) override
void handleModulesPicked(const QSet< u32 > &gats) override
void stdError(QString txt)
QString errorMessage() const
void setInput(const QVariant &inp)
void stdOutput(QString txt)
void requireInput(int type, QString prompt, QVariant defaultValue)
const QString & stdoutBuffer() const
int numberSelectedGates() const
int numberSelectedModules() const
#define log_error(channel,...)
#define log_info(channel,...)
#define log_warning(channel,...)
const Module * module(const Gate *g, const NodeBoxes &boxes)
T rtrim(const T &s, const char *to_remove=" \t\r\n")
std::filesystem::path get_library_directory()
std::vector< std::filesystem::path > get_plugin_directories()
SelectionRelay * gSelectionRelay
std::shared_ptr< Netlist > gNetlistOwner
grammar _PyParser_Grammar
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QSet::const_iterator constBegin() const const
QSet::iterator insert(const T &value)
bool isEmpty() const const
QString fromStdString(const std::string &str)
bool isEmpty() const const
std::string toStdString() const const
void setReadOnly(bool ro)
void start(QThread::Priority priority)
bool wait(QDeadlineTimer deadline)
int toInt(bool *ok) const const
QString toString() const const