Salome HOME
e684bf1e6c11cdc1d1aa4b9f331d4343448daf62
[modules/gui.git] / src / PyConsole / PyConsole_Console.cxx
1 // Copyright (C) 2007-2015  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
23 // File   : PyConsole_Console.cxx
24 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
25 //
26 /*!
27   \class PyConsole_Console
28   \brief Python console widget.
29 */  
30
31 #include "PyConsole_Interp.h"   /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!!
32 #include "PyConsole_Console.h"
33 #include "PyConsole_EnhEditor.h"
34 #include "PyConsole_EnhInterp.h"
35
36 #include <Qtx.h>
37
38 #include <QAction>
39 #include <QApplication>
40 #include <QClipboard>
41 #include <QEvent>
42 #include <QMenu>
43 #include <QVBoxLayout>
44
45 /*!
46   \brief Constructor.
47
48   Creates new python console widget.
49   \param parent parent widget
50   \param interp python interpreter
51 */
52 PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp )
53 : QWidget( parent )
54 {
55   PyConsole_Interp* anInterp = interp ? interp : new PyConsole_Interp();
56   
57   // initialize Python interpretator
58   anInterp->initialize();
59   
60   // create editor console
61   QVBoxLayout* lay = new QVBoxLayout( this );
62   lay->setMargin( 0 );
63   myEditor = new PyConsole_Editor( anInterp, this );
64   char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
65   if (synchronous && atoi(synchronous))
66   {
67       myEditor->setIsSync(true);
68   }
69   myEditor->viewport()->installEventFilter( this );
70   lay->addWidget( myEditor );
71
72   createActions();
73 }
74
75 /**
76  * Protected constructor.
77  */
78 PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* /*i*/,  PyConsole_Editor* e )
79   : QWidget (parent), myEditor(e)
80 {}
81
82 /*!
83   \brief Destructor.
84
85   Does nothing for the moment.
86 */
87 PyConsole_Console::~PyConsole_Console()
88 {
89 }
90
91 PyConsole_Interp* PyConsole_Console::getInterp() const
92 {
93   return myEditor ? myEditor->getInterp() : 0;
94
95
96 /*!
97   \brief Execute python command in the interpreter.
98   \param command string with command and arguments
99 */
100 void PyConsole_Console::exec( const QString& command )
101 {
102   if ( myEditor )
103     myEditor->exec( command );
104 }
105
106 /*!
107   \brief Execute python command in the interpreter 
108          and wait until it is finished.
109   
110   Block execution of main application until the python command is executed.
111   \param command string with command and arguments
112 */
113 void PyConsole_Console::execAndWait( const QString& command )
114 {
115   if ( myEditor )
116     myEditor->execAndWait( command );
117 }
118
119 /*!
120   \brief Get synchronous mode flag value.
121   
122   \sa setIsSync()
123   \return True if python console works in synchronous mode
124 */
125 bool PyConsole_Console::isSync() const
126 {
127   return myEditor->isSync();
128 }
129
130 /*!
131   \brief Set synchronous mode flag value.
132
133   In synhronous mode the Python commands are executed in the GUI thread
134   and the GUI is blocked until the command is finished. In the asynchronous
135   mode each Python command is executed in the separate thread that does not
136   block the main GUI loop.
137
138   \param on synhronous mode flag
139 */
140 void PyConsole_Console::setIsSync( const bool on )
141 {
142   myEditor->setIsSync( on );
143 }
144
145 /*!
146   \brief Get suppress output flag value.
147   
148   \sa setIsSuppressOutput()
149   \return True if python console output is suppressed.
150 */
151 bool PyConsole_Console::isSuppressOutput() const
152 {
153   return myEditor->isSuppressOutput();
154 }
155
156 /*!
157   \brief Set suppress output flag value.
158
159   In case if suppress output flag is true, the python 
160   console output suppressed.
161
162   \param on suppress output flag
163 */
164 void PyConsole_Console::setIsSuppressOutput( const bool on )
165 {
166   myEditor->setIsSuppressOutput(on);
167 }
168
169 /*!
170   \brief Get 'show banner' flag value.
171   
172   \sa setIsShowBanner()
173   \return \c true if python console shows banner
174 */
175 bool PyConsole_Console::isShowBanner() const
176 {
177   return myEditor->isShowBanner();
178 }
179
180 /*!
181   \brief Set 'show banner' flag value.
182
183   The banner is shown in the top of the python console window.
184
185   \sa isShowBanner()
186   \param on 'show banner' flag
187 */
188 void PyConsole_Console::setIsShowBanner( const bool on )
189 {
190   myEditor->setIsShowBanner( on );
191 }
192
193 /*!
194   \brief Change the python console's font.
195   \param f new font
196 */
197 void PyConsole_Console::setFont( const QFont& f )
198 {
199   if( myEditor )
200     myEditor->setFont( f );
201 }
202
203 /*!
204   \brief Get python console font.
205   \return current python console's font
206 */
207 QFont PyConsole_Console::font() const
208 {
209   QFont res;
210   if( myEditor )
211     res = myEditor->font();
212   return res;
213 }
214
215 /*!
216   \brief Event handler.
217
218   Handles context menu request event.
219
220   \param o object
221   \param e event
222   \return True if the event is processed and further processing should be stopped
223 */
224 bool PyConsole_Console::eventFilter( QObject* o, QEvent* e )
225 {
226   if ( o == myEditor->viewport() && e->type() == QEvent::ContextMenu )
227   {
228     contextMenuRequest( (QContextMenuEvent*)e );
229     return true;
230   }
231   return QWidget::eventFilter( o, e );
232 }
233
234 /*!
235   \brief Create the context popup menu.
236
237   Fill in the popup menu with the commands.
238
239   \param menu context popup menu
240 */
241 void PyConsole_Console::contextMenuPopup( QMenu* menu )
242 {
243   if ( myEditor->isReadOnly() )
244     return;
245
246   menu->addAction( myActions[CopyId] );
247   menu->addAction( myActions[PasteId] );
248   menu->addAction( myActions[ClearId] );
249   menu->addSeparator();
250   menu->addAction( myActions[SelectAllId] );
251   menu->addSeparator();
252   menu->addAction( myActions[DumpCommandsId] );
253   if ( !myEditor->isLogging() )
254     menu->addAction( myActions[StartLogId] );
255   else
256     menu->addAction( myActions[StopLogId] );
257
258   Qtx::simplifySeparators( menu );
259
260   updateActions();
261 }
262
263 /*!
264   \brief Set actions to be visible in the context popup menu.
265   
266   Actions, which IDs are set in \a flags parameter, will be shown in the 
267   context popup menu. Other actions will not be shown.
268
269   \param flags ORed together actions flags
270 */
271 void PyConsole_Console::setMenuActions( const int flags )
272 {
273   myActions[CopyId]->setVisible( flags & CopyId );
274   myActions[PasteId]->setVisible( flags & PasteId );
275   myActions[ClearId]->setVisible( flags & ClearId );
276   myActions[SelectAllId]->setVisible( flags & SelectAllId );
277   myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId );
278   myActions[StartLogId]->setVisible( flags & StartLogId );
279   myActions[StopLogId]->setVisible( flags & StopLogId );
280 }
281
282 /*!
283   \brief Get menu actions which are currently visible in the context popup menu.
284   \return ORed together actions flags
285   \sa setMenuActions()
286 */
287 int PyConsole_Console::menuActions() const
288 {
289   int ret = 0;
290   ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 );
291   ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 );
292   ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 );
293   ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 );
294   ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 );
295   ret = ret | ( myActions[StartLogId]->isVisible() ? StartLogId : 0 );
296   ret = ret | ( myActions[StopLogId]->isVisible() ? StopLogId : 0 );
297   return ret;
298 }
299
300 /*!
301   \brief Create menu actions.
302
303   Create context popup menu actions.
304 */
305 void PyConsole_Console::createActions()
306 {
307   QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this );
308   a->setStatusTip( tr( "EDIT_COPY_CMD" ) );
309   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) );
310   myActions.insert( CopyId, a );
311
312   a = new QAction( tr( "EDIT_PASTE_CMD" ), this );
313   a->setStatusTip( tr( "EDIT_PASTE_CMD" ) );
314   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) );
315   myActions.insert( PasteId, a );
316
317   a = new QAction( tr( "EDIT_CLEAR_CMD" ), this );
318   a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) );
319   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) );
320   myActions.insert( ClearId, a );
321
322   a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this );
323   a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) );
324   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) );
325   myActions.insert( SelectAllId, a );
326   
327   a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this );
328   a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) );
329   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) );
330   myActions.insert( DumpCommandsId, a );
331
332   a = new QAction( tr( "EDIT_STARTLOG_CMD" ), this );
333   a->setStatusTip( tr( "EDIT_STARTLOG_CMD" ) );
334   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( startLog() ) );
335   myActions.insert( StartLogId, a );
336
337   a = new QAction( tr( "EDIT_STOPLOG_CMD" ), this );
338   a->setStatusTip( tr( "EDIT_STOPLOG_CMD" ) );
339   connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( stopLog() ) );
340   myActions.insert( StopLogId, a );
341 }
342
343 /*!
344   \brief Update menu actions.
345
346   Update context popup menu action state.
347 */
348 void PyConsole_Console::updateActions()
349 {
350   myActions[CopyId]->setEnabled( myEditor->textCursor().hasSelection() );
351   myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() );
352   myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() );
353 }
354
355 /*!
356   \brief Start python trace logging
357   \param fileName the path to the log file
358 */
359 void PyConsole_Console::startLog( const QString& fileName )
360 {
361   myEditor->startLog( fileName );
362 }
363
364 /*!
365   \brief Stop python trace logging
366 */
367 void PyConsole_Console::stopLog()
368 {
369   myEditor->stopLog();
370 }
371
372 /**
373  * Similar to constructor of the base class but using enhanced objects.
374  * TODO: this should really be done in a factory to avoid code duplication.
375  * @param parent
376  * @param interp
377  */
378 PyConsole_EnhConsole::PyConsole_EnhConsole( QWidget* parent, PyConsole_Interp* interp )
379   : PyConsole_Console( parent, interp, 0 )
380 {
381   PyConsole_Interp* anInterp = interp ? interp : new PyConsole_EnhInterp();
382
383   // initialize Python interpretator
384   anInterp->initialize();
385
386   // create editor console
387   QVBoxLayout* lay = new QVBoxLayout( this );
388   lay->setMargin( 0 );
389   myEditor = new PyConsole_EnhEditor( anInterp, this );
390   char* synchronous = getenv("PYTHON_CONSOLE_SYNC");
391   if (synchronous && atoi(synchronous))
392   {
393       myEditor->setIsSync(true);
394   }
395   myEditor->viewport()->installEventFilter( this );
396   lay->addWidget( myEditor );
397
398   createActions();
399 }