Salome HOME
5d1da3cddc78f94aeecbfa5dfdca7db050979109
[modules/gui.git] / tools / PyConsole / src / PyConsole_Console.cxx
1 // Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 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 GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 // File   : PyConsole_Console.cxx
23 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
24
25 #include "PyConsole_Interp.h"
26 #include "PyConsole_Console.h"
27 #include "PyConsole_Editor.h"
28
29 #include <QAction>
30 #include <QApplication>
31 #include <QClipboard>
32 #include <QContextMenuEvent>
33 #include <QMenu>
34 #include <QVBoxLayout>
35
36 /*!
37   \class PyConsole_Console
38   \brief Python console widget.
39
40   To create a Python console, just use default contstructor, specifying only a parent widget:
41   \code
42   PyConsole_Console c(myWindow);
43   \endcode
44
45   This will create a console with default editor and interpreter.
46
47   To use custom editor with the console, you can use alternative constructor:
48   \code
49   PyConsole_Console c(myWindow, new MyEditor());
50   \endcode
51 */  
52
53 /*!
54   \brief Default constructor.
55
56   Creates new python console widget.
57   \param parent parent widget
58 */
59 PyConsole_Console::PyConsole_Console( QWidget* parent )
60 : QWidget( parent )
61 {
62   init( 0 );
63 }
64
65 /*!
66   \brief Constructor.
67
68   Creates new python console widget.
69   \param parent parent widget
70   \param editor python editor
71 */
72 PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Editor* editor )
73 : QWidget( parent )
74 {
75   init( editor );
76 }
77
78 /*!
79   \brief Initialize console widget.
80   \param editor python editor
81 */
82 void PyConsole_Console::init( PyConsole_Editor* editor )
83 {
84   // initialize Python interpretator
85   PyConsole_Interp* interp = editor ? editor->getInterp() : new PyConsole_Interp();
86   interp->initialize();
87   
88   // create editor console
89   QVBoxLayout* lay = new QVBoxLayout( this );
90   lay->setContentsMargins( 0, 0, 0 ,0 );
91   myEditor = editor ? editor : new PyConsole_Editor( this, interp );
92   myEditor->setContextMenuPolicy( Qt::NoContextMenu );
93   lay->addWidget( myEditor );
94
95   // force synchronous mode
96   QString synchronous = qgetenv( "PYTHON_CONSOLE_SYNC" );
97   if ( !synchronous.isEmpty() && synchronous.toInt() > 0 )
98     setIsSync( true );
99
100   // create actions
101   createActions();
102 }
103
104 /*!
105   \brief Destructor.
106 */
107 PyConsole_Console::~PyConsole_Console()
108 {
109 }
110
111 /*!
112   \brief Get Python interpreter
113   \return pointer to Python interpreter
114 */
115 PyConsole_Interp* PyConsole_Console::getInterp() const
116 {
117   return myEditor ? myEditor->getInterp() : 0;
118 }
119
120 /*!
121   \brief Execute python command in the interpreter.
122   \param command string with command and arguments
123 */
124 void PyConsole_Console::exec( const QString& command )
125 {
126   if ( myEditor )
127     myEditor->exec( command );
128 }
129
130 /*!
131   \brief Execute python command in the interpreter 
132          and wait until it is finished.
133   
134   Block execution of main application until the python command is executed.
135   \param command string with command and arguments
136 */
137 void PyConsole_Console::execAndWait( const QString& command )
138 {
139   if ( myEditor )
140     myEditor->execAndWait( command );
141 }
142
143 /*!
144   \brief Get synchronous mode flag value.
145   
146   \sa setIsSync()
147   \return \c true if python console works in synchronous mode
148 */
149 bool PyConsole_Console::isSync() const
150 {
151   return myEditor ? myEditor->isSync() : false;
152 }
153
154 /*!
155   \brief Set synchronous mode flag value.
156
157   In synhronous mode the Python commands are executed in the GUI thread
158   and the GUI is blocked until the command is finished. In the asynchronous
159   mode each Python command is executed in the separate thread that does not
160   block the main GUI loop.
161
162   \param on synhronous mode flag
163 */
164 void PyConsole_Console::setIsSync( const bool on )
165 {
166   if ( myEditor ) 
167     myEditor->setIsSync( on );
168 }
169
170 /*!
171   \brief Get suppress output flag value.
172   
173   \sa setIsSuppressOutput()
174   \return \c true if python console output is suppressed.
175 */
176 bool PyConsole_Console::isSuppressOutput() const
177 {
178   return myEditor ? myEditor->isSuppressOutput() : false;
179 }
180
181 /*!
182   \brief Set suppress output flag value.
183
184   In case if suppress output flag is \c true, the python 
185   console output suppressed.
186
187   \param on suppress output flag
188 */
189 void PyConsole_Console::setIsSuppressOutput( const bool on )
190 {
191   if ( myEditor )
192     myEditor->setIsSuppressOutput( on );
193 }
194
195 /*!
196   \brief Get 'show banner' flag value.
197   
198   \sa setIsShowBanner()
199   \return \c true if python console shows banner
200 */
201 bool PyConsole_Console::isShowBanner() const
202 {
203   return myEditor ? myEditor->isShowBanner() : false;
204 }
205
206 /*!
207   \brief Set 'show banner' flag value.
208
209   The banner is shown in the top of the python console window.
210
211   \sa isShowBanner()
212   \param on 'show banner' flag
213 */
214 void PyConsole_Console::setIsShowBanner( const bool on )
215 {
216   if ( myEditor )
217     myEditor->setIsShowBanner( on );
218 }
219
220 /*!
221   \brief Returns \c true if auto-completion feature is switched on
222   or \c false otherwise
223   \sa setAutoCompletion()
224 */
225 bool PyConsole_Console::autoCompletion() const
226 {
227   return myEditor ? myEditor->autoCompletion() : false;
228 }
229
230 /*!
231   \brief Switch on/off commands auto-completion feature
232   \sa autoCompletion()
233 */
234 void PyConsole_Console::setAutoCompletion( const bool on )
235 {
236   if ( myEditor )
237     myEditor->setAutoCompletion( on );
238 }
239
240 /*!
241   \brief Change the python console's font.
242   \param f new font
243 */
244 void PyConsole_Console::setFont( const QFont& f )
245 {
246   if ( myEditor )
247     myEditor->setFont( f );
248 }
249
250 /*!
251   \brief Get python console font.
252   \return current python console font
253 */
254 QFont PyConsole_Console::font() const
255 {
256   return myEditor ? myEditor->font() : QFont();
257 }
258
259 /*!
260   \brief Set actions to be visible in the context popup menu.
261   
262   Actions, which IDs are set in \a flags parameter, will be shown in the 
263   context popup menu. Other actions will not be shown.
264
265   \param flags ORed together actions flags
266 */
267 void PyConsole_Console::setMenuActions( const int flags )
268 {
269   myActions[CopyId]->setVisible( flags & CopyId );
270   myActions[PasteId]->setVisible( flags & PasteId );
271   myActions[ClearId]->setVisible( flags & ClearId );
272   myActions[SelectAllId]->setVisible( flags & SelectAllId );
273   myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId );
274   myActions[StartLogId]->setVisible( flags & StartLogId );
275   myActions[StopLogId]->setVisible( flags & StopLogId );
276 }
277
278 /*!
279   \brief Get menu actions which are currently visible in the context popup menu.
280   \return ORed together actions flags
281   \sa setMenuActions()
282 */
283 int PyConsole_Console::menuActions() const
284 {
285   int ret = 0;
286   ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 );
287   ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 );
288   ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 );
289   ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 );
290   ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 );
291   ret = ret | ( myActions[StartLogId]->isVisible() ? StartLogId : 0 );
292   ret = ret | ( myActions[StopLogId]->isVisible() ? StopLogId : 0 );
293   return ret;
294 }
295
296 /*!
297   \brief Create menu actions.
298
299   Create context popup menu actions.
300 */
301 void PyConsole_Console::createActions()
302 {
303   QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this );
304   a->setStatusTip( tr( "EDIT_COPY_CMD" ) );
305   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
306   myActions.insert( CopyId, a );
307
308   a = new QAction( tr( "EDIT_PASTE_CMD" ), this );
309   a->setStatusTip( tr( "EDIT_PASTE_CMD" ) );
310   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
311   myActions.insert( PasteId, a );
312
313   a = new QAction( tr( "EDIT_CLEAR_CMD" ), this );
314   a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) );
315   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) );
316   myActions.insert( ClearId, a );
317
318   a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this );
319   a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) );
320   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
321   myActions.insert( SelectAllId, a );
322   
323   a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this );
324   a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) );
325   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) );
326   myActions.insert( DumpCommandsId, a );
327
328   a = new QAction( tr( "EDIT_STARTLOG_CMD" ), this );
329   a->setStatusTip( tr( "EDIT_STARTLOG_CMD" ) );
330   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( startLog() ) );
331   myActions.insert( StartLogId, a );
332
333   a = new QAction( tr( "EDIT_STOPLOG_CMD" ), this );
334   a->setStatusTip( tr( "EDIT_STOPLOG_CMD" ) );
335   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( stopLog() ) );
336   myActions.insert( StopLogId, a );
337 }
338
339 /*!
340   \brief Update menu actions.
341
342   Update context popup menu action state.
343 */
344 void PyConsole_Console::updateActions()
345 {
346   myActions[CopyId]->setEnabled( myEditor && myEditor->textCursor().hasSelection() );
347   myActions[PasteId]->setEnabled( myEditor && !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() );
348   myActions[SelectAllId]->setEnabled( myEditor && !myEditor->document()->isEmpty() );
349 }
350
351 /*!
352   \brief Start python trace logging
353   \param fileName the path to the log file
354 */
355 void PyConsole_Console::startLog( const QString& fileName )
356 {
357   if ( myEditor ) 
358     myEditor->startLog( fileName );
359 }
360
361 /*!
362   \brief Stop python trace logging
363 */
364 void PyConsole_Console::stopLog()
365 {
366   if ( myEditor ) 
367     myEditor->stopLog();
368 }
369
370 /*!
371   \brief Process context popup menu request
372
373   Show the context popup menu.
374
375   \param event context popup menu event
376 */
377 void PyConsole_Console::contextMenuEvent( QContextMenuEvent* event )
378 {
379   if ( !myEditor || myEditor->isReadOnly() )
380     return;
381
382   QMenu* menu = new QMenu( this );
383  
384   menu->addAction( myActions[CopyId] );
385   menu->addAction( myActions[PasteId] );
386   menu->addAction( myActions[ClearId] );
387   menu->addSeparator();
388   menu->addAction( myActions[SelectAllId] );
389   menu->addSeparator();
390   menu->addAction( myActions[DumpCommandsId] );
391   if ( !myEditor->isLogging() )
392     menu->addAction( myActions[StartLogId] );
393   else
394     menu->addAction( myActions[StopLogId] );
395
396   updateActions();
397
398   menu->exec( event->globalPos());
399
400   delete menu;
401 }