KiCad PCB EDA Suite
pcbnew_action_plugins.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
29 #include "pcbnew_action_plugins.h"
30 #include <python_scripting.h>
31 #include <stdio.h>
32 #include <macros.h>
33 #include <pcbnew_id.h>
34 #include <menus_helpers.h>
35 #include <class_drawpanel.h> // m_canvas
36 #include <class_board.h>
37 #include <class_module.h>
38 #include <class_track.h>
39 #include <class_drawsegment.h>
40 #include <class_zone.h>
41 #include <board_commit.h>
42 #include <kicad_device_context.h>
43 
45 {
46  PyLOCK lock;
47 
48  this->m_PyAction = aAction;
49  Py_XINCREF( aAction );
50 }
51 
52 
54 {
55  PyLOCK lock;
56 
57  Py_XDECREF( this->m_PyAction );
58 }
59 
60 
61 PyObject* PYTHON_ACTION_PLUGIN::CallMethod( const char* aMethod, PyObject* aArglist )
62 {
63  PyLOCK lock;
64 
65  PyErr_Clear();
66  // pFunc is a new reference to the desired method
67  PyObject* pFunc = PyObject_GetAttrString( this->m_PyAction, aMethod );
68 
69  if( pFunc && PyCallable_Check( pFunc ) )
70  {
71  PyObject* result = PyObject_CallObject( pFunc, aArglist );
72 
73  if( PyErr_Occurred() )
74  {
75  wxMessageBox( PyErrStringWithTraceback(),
76  _( "Exception on python action plugin code" ),
77  wxICON_ERROR | wxOK );
78  }
79 
80  if( result )
81  {
82  Py_XDECREF( pFunc );
83  return result;
84  }
85  }
86  else
87  {
88  wxString msg = wxString::Format(_( "Method \"%s\" not found, or not callable" ), aMethod );
89  wxMessageBox( msg, _( "Unknown Method" ), wxICON_ERROR | wxOK );
90  }
91 
92  if( pFunc )
93  {
94  Py_XDECREF( pFunc );
95  }
96 
97  return NULL;
98 }
99 
100 
101 wxString PYTHON_ACTION_PLUGIN::CallRetStrMethod( const char* aMethod, PyObject* aArglist )
102 {
103  wxString ret;
104  PyLOCK lock;
105 
106  PyObject* result = CallMethod( aMethod, aArglist );
107 
108  if( result )
109  {
110  const char* str_res = PyString_AsString( result );
111  ret = FROM_UTF8( str_res );
112  Py_DECREF( result );
113  }
114 
115  return ret;
116 }
117 
118 
120 {
121  PyLOCK lock;
122 
123  return CallRetStrMethod( "GetCategoryName" );
124 }
125 
126 
128 {
129  PyLOCK lock;
130 
131  return CallRetStrMethod( "GetName" );
132 }
133 
134 
136 {
137  PyLOCK lock;
138 
139  return CallRetStrMethod( "GetDescription" );
140 }
141 
142 
144 {
145  PyLOCK lock;
146 
147  CallMethod( "Run" );
148 }
149 
150 
152 {
153  return (void*) m_PyAction;
154 }
155 
156 
157 void PYTHON_ACTION_PLUGINS::register_action( PyObject* aPyAction )
158 {
159  PYTHON_ACTION_PLUGIN* fw = new PYTHON_ACTION_PLUGIN( aPyAction );
160 
161  fw->register_action();
162 }
163 
164 
165 void PYTHON_ACTION_PLUGINS::deregister_action( PyObject* aPyAction )
166 {
167  // deregister also destroyes the previously created "PYTHON_ACTION_PLUGIN object"
168  ACTION_PLUGINS::deregister_object( (void*) aPyAction );
169 }
170 
171 
172 #if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
173 
174 void PCB_EDIT_FRAME::OnActionPlugin( wxCommandEvent& aEvent )
175 {
176  int id = aEvent.GetId();
177 
178  ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByMenu( id );
179 
180  if( actionPlugin )
181  {
182  PICKED_ITEMS_LIST itemsList;
183  BOARD* currentPcb = GetBoard();
184  bool fromEmpty = false;
185 
186  itemsList.m_Status = UR_CHANGED;
187 
188  OnModify();
189 
190  // Append tracks:
191  for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
192  {
193  ITEM_PICKER picker( item, UR_CHANGED );
194  itemsList.PushItem( picker );
195  }
196 
197  // Append modules:
198  for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
199  {
200  ITEM_PICKER picker( item, UR_CHANGED );
201  itemsList.PushItem( picker );
202  }
203 
204  // Append drawings
205  for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
206  {
207  ITEM_PICKER picker( item, UR_CHANGED );
208  itemsList.PushItem( picker );
209  }
210 
211  // Append zones outlines
212  for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
213  {
214  ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
215  ii ), UR_CHANGED );
216  itemsList.PushItem( picker );
217  }
218 
219  // Append zones segm:
220  for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
221  {
222  ITEM_PICKER picker( item, UR_CHANGED );
223  itemsList.PushItem( picker );
224  }
225 
226  if( itemsList.GetCount() > 0 )
227  SaveCopyInUndoList( itemsList, UR_CHANGED, wxPoint( 0.0, 0.0 ) );
228  else
229  fromEmpty = true;
230 
231  itemsList.ClearItemsList();
232 
233  // Execute plugin himself...
235  actionPlugin->Run();
237 
238  currentPcb->m_Status_Pcb = 0;
239 
240  // Get back the undo buffer to fix some modifications
241  PICKED_ITEMS_LIST* oldBuffer = NULL;
242 
243  if( fromEmpty )
244  {
245  oldBuffer = new PICKED_ITEMS_LIST();
246  oldBuffer->m_Status = UR_NEW;
247  }
248  else
249  {
250  oldBuffer = GetScreen()->PopCommandFromUndoList();
251  wxASSERT( oldBuffer );
252  }
253 
254  // Try do discover what was modified
255 
256  PICKED_ITEMS_LIST deletedItemsList;
257 
258  // Found deleted modules
259  for( unsigned int i = 0; i < oldBuffer->GetCount(); i++ )
260  {
261  BOARD_ITEM* item = (BOARD_ITEM*) oldBuffer->GetPickedItem( i );
262  ITEM_PICKER picker( item, UR_DELETED );
263 
264  wxASSERT( item );
265 
266  switch( item->Type() )
267  {
268  case PCB_NETINFO_T:
269  case PCB_MARKER_T:
270  case PCB_MODULE_T:
271  case PCB_TRACE_T:
272  case PCB_VIA_T:
273  case PCB_LINE_T:
274  case PCB_TEXT_T:
275  case PCB_DIMENSION_T:
276  case PCB_TARGET_T:
277  case PCB_ZONE_T:
278 
279  // If item has a list it's mean that the element is on the board
280  if( item->GetList() == NULL )
281  {
282  deletedItemsList.PushItem( picker );
283  }
284 
285  break;
286 
287  case PCB_ZONE_AREA_T:
288  {
289  bool zoneFound = false;
290 
291  for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
292  zoneFound |= currentPcb->GetArea( ii ) == item;
293 
294  if( !zoneFound )
295  {
296  deletedItemsList.PushItem( picker );
297  }
298 
299  break;
300  }
301 
302  default:
303  wxString msg;
304  msg.Printf( _( "(PCB_EDIT_FRAME::OnActionPlugin) needs work: "
305  "BOARD_ITEM type (%d) not handled" ),
306  item->Type() );
307  wxFAIL_MSG( msg );
308  break;
309  }
310  }
311 
312  // Mark deleted elements in undolist
313  for( unsigned int i = 0; i < deletedItemsList.GetCount(); i++ )
314  {
315  oldBuffer->PushItem( deletedItemsList.GetItemWrapper( i ) );
316  }
317 
318  // Find new modules
319  for( BOARD_ITEM* item = currentPcb->m_Modules; item != NULL; item = item->Next() )
320  {
321  if( !oldBuffer->ContainsItem( item ) )
322  {
323  ITEM_PICKER picker( item, UR_NEW );
324  oldBuffer->PushItem( picker );
325  }
326  }
327 
328  for( BOARD_ITEM* item = currentPcb->m_Track; item != NULL; item = item->Next() )
329  {
330  if( !oldBuffer->ContainsItem( item ) )
331  {
332  ITEM_PICKER picker( item, UR_NEW );
333  oldBuffer->PushItem( picker );
334  }
335  }
336 
337  for( BOARD_ITEM* item = currentPcb->m_Drawings; item != NULL; item = item->Next() )
338  {
339  if( !oldBuffer->ContainsItem( item ) )
340  {
341  ITEM_PICKER picker( item, UR_NEW );
342  oldBuffer->PushItem( picker );
343  }
344  }
345 
346  for( BOARD_ITEM* item = currentPcb->m_SegZoneDeprecated; item != NULL; item = item->Next() )
347  {
348  if( !oldBuffer->ContainsItem( item ) )
349  {
350  ITEM_PICKER picker( item, UR_NEW );
351  oldBuffer->PushItem( picker );
352  }
353  }
354 
355  for( int ii = 0; ii < currentPcb->GetAreaCount(); ii++ )
356  {
357  if( !oldBuffer->ContainsItem( (EDA_ITEM*) currentPcb->GetArea( ii ) ) )
358  {
359  ITEM_PICKER picker( (EDA_ITEM*) currentPcb->GetArea(
360  ii ), UR_NEW );
361  oldBuffer->PushItem( picker );
362  }
363  }
364 
365 
366  GetScreen()->PushCommandToUndoList( oldBuffer );
367 
368  if( IsGalCanvasActive() )
369  {
370  UseGalCanvas( GetGalCanvas() );
371  }
372  else
373  {
375  GetScreen()->SetModify();
376  Refresh();
377  }
378  }
379 }
380 
381 
382 void PCB_EDIT_FRAME::RebuildActionPluginMenus()
383 {
384  wxMenu* actionMenu = GetMenuBar()->FindItem( ID_TOOLBARH_PCB_ACTION_PLUGIN )->GetSubMenu();
385 
386  if( !actionMenu ) // Should not occur.
387  return;
388 
389  // First, remove existing submenus, if they are too many
390  wxMenuItemList list = actionMenu->GetMenuItems();
391  // The first menuitems are the refresh menu and separator. do not count them
392  int act_menu_count = -2;
393 
394  std::vector<wxMenuItem*> available_menus;
395 
396  for( auto iter = list.begin(); iter != list.end(); ++iter, act_menu_count++ )
397  {
398  if( act_menu_count < 0 )
399  continue;
400 
401  wxMenuItem* item = *iter;
402 
403  if( act_menu_count < ACTION_PLUGINS::GetActionsCount() )
404  {
405  available_menus.push_back( item );
406  continue;
407  }
408 
409  // Remove menus which are not usable for our current plugin list
410  Disconnect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
411  (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
412  PCB_EDIT_FRAME::OnActionPlugin );
413  actionMenu->Delete( item );
414  }
415 
416  for( int ii = 0; ii < ACTION_PLUGINS::GetActionsCount(); ii++ )
417  {
418  wxMenuItem* item;
419 
420  if( ii < (int) available_menus.size() )
421  {
422  item = available_menus[ii];
423  item->SetItemLabel( ACTION_PLUGINS::GetAction( ii )->GetName() );
424  item->SetHelp( ACTION_PLUGINS::GetAction( ii )->GetDescription() );
425  }
426  else
427  {
428  item = AddMenuItem( actionMenu, wxID_ANY,
431  KiBitmap( hammer_xpm ) );
432 
433  Connect( item->GetId(), wxEVT_COMMAND_MENU_SELECTED,
434  (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &
435  PCB_EDIT_FRAME::OnActionPlugin );
436  }
437 
438  ACTION_PLUGINS::SetActionMenu( ii, item->GetId() );
439  }
440 }
441 
442 
443 #endif
void UpdateUserInterface()
Update the layer manager and other widgets from the board setup (layer and items visibility, colors ...) (note: it is automatically called by action plugins, after running the plugin, so call this function is usually not needed inside action plugins.
DHEAD * GetList() const
Definition: base_struct.h:220
KICAD_T Type() const
Function Type()
Definition: base_struct.h:209
Class ACTION_PLUGIN This is the parent class from where any action plugin class must derive...
Definition: action_plugin.h:40
wxString GetName() override
Function GetName.
wxString CallRetStrMethod(const char *aMethod, PyObject *aArglist=NULL)
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes...
Definition: macros.h:53
Class BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class...
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Function AddMenuItem is an inline helper function to create and insert a menu item with an icon into ...
Definition: bitmap.cpp:174
Class BOARD to handle a board.
MODULE * Next() const
Definition: class_module.h:121
class ZONE_CONTAINER, a zone area
Definition: typeinfo.h:102
class TEXTE_PCB, text on a layer
Definition: typeinfo.h:92
Classes to handle copper zones.
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
void register_action()
Function register_action It&#39;s the standard method of a "ACTION_PLUGIN" to register itself into the AC...
Functions relatives to tracks, vias and segments used to fill zones.
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
This file contains miscellaneous commonly used macros and functions.
SEGZONE * Next() const
Definition: class_track.h:366
BOARD_ITEM * Next() const
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Function KiBitmap constructs a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:78
bool ContainsItem(const EDA_ITEM *aItem) const
Function IsItemInList.
class MODULE, a footprint
Definition: typeinfo.h:89
static void deregister_action(PyObject *aPyAction)
void * GetObject() override
Function GetObject This method gets the pointer to the object from where this action constructs...
a helper to handle the real device context used in KiCad
wxString GetDescription() override
Function GetDescription.
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
virtual void Run()=0
Function Run This method the the action.
static ACTION_PLUGIN * GetActionByMenu(int aMenu)
Function GetActionByMenu find action plugin associated to a menu id.
DLIST< SEGZONE > m_SegZoneDeprecated
Definition: class_board.h:249
void Run() override
Function Run This method the the action.
static int GetActionsCount()
Function GetActionsCount.
DLIST< BOARD_ITEM > m_Drawings
Definition: class_board.h:243
class SEGZONE, a segment used to fill a zone area (segment on a copper layer)
Definition: typeinfo.h:97
PYTHON_ACTION_PLUGIN(PyObject *action)
int GetAreaCount() const
Function GetAreaCount.
Definition: class_board.h:1015
Class PICKED_ITEMS_LIST is a holder to handle information on schematic or board items.
PyObject * CallMethod(const char *aMethod, PyObject *aArglist=NULL)
class DIMENSION, a dimension (graphic item)
Definition: typeinfo.h:100
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:101
unsigned GetCount() const
Function GetCount.
BOARD * GetBoard()
static ACTION_PLUGIN * GetAction(const wxString &aName)
Function GetAction.
class MARKER_PCB, a marker used to show something
Definition: typeinfo.h:99
TRACK * Next() const
Definition: class_track.h:99
ZONE_CONTAINER * GetArea(int index) const
Function GetArea returns the Area (Zone Container) at a given index.
Definition: class_board.h:986
Class to handle a graphic segment.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
Function GetPickedItem.
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:169
static void SetActionMenu(int aIndex, int idMenu)
Function SetActionMenu Associate a menu id to an action plugin.
DLIST< MODULE > m_Modules
Definition: class_board.h:247
Class PCBNEW_ACTION_PLUGINS.
class NETINFO_ITEM, a description of a net
Definition: typeinfo.h:104
size_t i
Definition: json11.cpp:597
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:162
wxString PyErrStringWithTraceback()
void ClearItemsList()
Function ClearItemsList deletes only the list of pickers, NOT the picked data itself.
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
DLIST< TRACK > m_Track
Definition: class_board.h:248
Module description (excepted pads)
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
static void register_action(PyObject *aPyAction)
ITEM_PICKER GetItemWrapper(unsigned int aIdx) const
Function GetItemWrapper.
static bool deregister_object(void *aObject)
Function deregister_object Anyone calls this method to deregister an object which builds a action...
static void SetActionRunning(bool aRunning)
Function SetActionRunning.
wxString GetCategoryName() override
Function GetCategoryName.
int m_Status_Pcb
Flags used in ratsnest calculation and update.
Definition: class_board.h:239