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