1 from PyQt4.QtGui import *
2 from PyQt4.QtCore import *
3
4 import re
5
6 import Items
7 import Actions
8
10 """
11 Overview
12 ========
13 This class holds a graphical representation (in a tree format) of all the BASIN objects
14 currently accounted for by the GUI. It will only keep track of those BASIN objects that
15 were made while in %autopx mode (IPython1's auto-parallel mode).
16
17 The general way that it does this is by relying on the BASIN_ipython_shell.IPythonView
18 object for a few things. First, the IPythonView must be able to catch each time a top-level
19 region is created. Then it must stored that information on the parallel engine by appending
20 the name of this new top-level region to a variable named BASIN_TOPLEVEL_REGION_LIST. It
21 is a newline deliminated string containing the names of top-level Regions.
22
23 Since Regions are able to self-examine themselves, all
24 that needs to be stored is their variable name. From
25 that it can get the total information about objects
26 that are children to it. From their it constructs the
27 tree using the following classes:
28 - Region
29 - Grid
30 - List
31 - Attribute
32 """
33
34 - def __init__ (self, layout, parent, bridge):
35 """
36
37 This sets up the QTreeWidget to have 4 columns:
38 1. Data - The name of the data
39 2. Type - It's BASIN object type
40 3. Dimensions - Applicable only to BASIN Attributes
41 4. Alias - The name of the alias, if it exists
42 Aliases are actual python variables
43 that reference the represented
44
45 @param layout: The parent's layout. This will be automatically added to the layout.
46 @type layout:
47
48 @param parent: The parent object of this tree.
49 @type parent:
50
51 @param bridge: The overall connecting bridge class which gets set to self._bridge
52 @type bridge: L{Bridge<client.BASIN_bridge.Bridge>}
53 """
54
55 QTreeWidget.__init__ (self, parent)
56
57 self._bridge = bridge
58 bridge.setRegionViewer (self)
59
60 self.setAnimated (False)
61 headers = QStringList ("Data")
62 headers.append ("Type")
63 headers.append ("Dimensions")
64 headers.append ("Alias")
65 self.setHeaderLabels (headers)
66 self.setDragEnabled (True)
67 self.setAcceptDrops (True)
68 self._copy = None
69
70 self.makeBasin ()
71
72 box = QGroupBox ("BASIN Data Structure")
73 boxLayout = QHBoxLayout (box)
74 boxLayout.addWidget (self)
75 layout.addWidget (box)
76
77 - def Update (self, topRegions):
78 """
79 Overview
80 ========
81 This should only ever be called from the BASIN_bridge.Bridge class.
82
83 It clears out all current entries and then re-populates them based off of a (hopefully)
84 new BASIN_TOPLEVEL_REGION_LIST.
85
86 Presumably this program also uses a BASIN_variable_viewer.VariableList object.
87 In such a case, this must be updated BEFORE the VariableList is, and thus it should
88 really only be handled at one point, thus I let the bridge handle Update.
89 """
90
91 self.clear ()
92 self._copy = None
93 self.makeBasin ()
94
95 for region in topRegions:
96 self.MakeTopRegion (region)
97
98 self.expandItem (self.top)
99 self.resizeColumnToContents (2)
100 self.resizeColumnToContents (1)
101 self.resizeColumnToContents (0)
102
104 """
105 Overview
106 ========
107 This will retrieve the item currently selected by the user. It is possible (although unlikely)
108 that more than one, or none, may be selected. In such a case, a False signal is returned.
109 """
110
111 items = self.selectedItems ()
112 if len (items) == 1:
113 return items[0]
114
116 """
117 Overview
118 ========
119 This is called initially, and after each Update. All it doesi s create the top-most object in the
120 tree structure. It has no real value, just a place-holder for everything else to go under, much
121 like a 'root' that must exist first, regardless of anything.
122 """
123
124 self.top = QTreeWidgetItem ()
125 self.top.setText (0, "BASIN")
126 self.top.setFlags (Qt.ItemIsDropEnabled)
127 self.addTopLevelItem (self.top)
128
130 """
131 Overview
132 ========
133 This will be called by Update for as many times as there are top-level Regions. It will take the
134 name of said Region and call its `basin_kernel` method `Region::get_client_information`. This
135 generates a string containing information about all sub-objects of this Region.
136
137 All This really does is take the name of the Region, and this information string and pass
138 it to the constructor of BASIN_region_viewer.TopRegion along with the reference to
139 the top-most root in the tree, where new top-level regions will be under.
140 """
141
142 realResult = []
143 result = self._bridge._ipyShell.SilentCommand ("print \"\\n\" + " + var +
144 ".get_client_information ()").split ("\n")[3:]
145
146
147
148 if result.count ("") > 0:
149 result = result[:result.index ("")]
150
151 regexp = re.compile (".*\[.*\].*(In|Out).*\[.*\]")
152 for line in result:
153 if not regexp.match (line):
154 realResult.append (line)
155
156 Items.TopRegion (self.top, realResult, var)
157
159 """
160 Overview
161 ========
162 This is used to initiate a drag sequence. Basically a drag will only start when the user
163 has pressed on an object, and moved a minimum distance. This is to prevent slight movements
164 during double-clicks as being interepreted incorrectly.
165 """
166
167 if (ev.globalPos () - self._dragStartPosition).manhattanLength () > \
168 QApplication.startDragDistance ():
169 object = self.getSingleSelectedItem ()
170 if object != None:
171 mime = QMimeData ()
172 mime.setData (QString ("text/plain"), str (object._name + "\n" +
173 object._shellAccess + "\n" + object.Type ()))
174 drag = QDrag (self)
175 drag.setMimeData (mime)
176 self._bridge._drugItem = object
177 dropAction = drag.start (Qt.LinkAction)
178
180 """
181 Overview
182 ========
183 Captures the initial position of a mouse button being pressed. The onyl real use for
184 this is in conjunction with mouseMoveEvent, which will initiate a drag sequence if the
185 mouse has moved a minimum distance from this initial point.
186 """
187
188 QTreeWidget.mousePressEvent (self, ev)
189 self._dragStartPosition = ev.pos ()
190 item = self.getSingleSelectedItem ()
191 if item and item.Type () == "REGION":
192 self._bridge._actions.menuRegionSelected ()
193 else:
194 self._bridge._actions.menuRegionNotSelected ()
195
197 """
198 Overview
199 ========
200 This method merely grabs the currently selected item and sends it on its way to the
201 BASIN_dock.Dock. Presumably the Dock can handle it from there.
202 """
203
204 self._bridge.AddToDock (self.getSingleSelectedItem ())
205
207 """
208 Overview
209 ========
210 This method will check to see if the user released the right button of the mouse while
211 over the RegionTree. If so, it sends the position of the release to the appropriate
212 handling method, rightClick.
213 """
214
215 QTreeWidget.mouseReleaseEvent (self, ev)
216 if ev.button () == Qt.RightButton:
217 self.rightClick (ev.globalPos ())
218
220 """
221 Overview
222 ========
223 This method is obviously called on a right-click and drops down a menu generated by this function.
224
225 It depends on the exact object you clicked on, for what options will appear in the menu.
226
227 Generally speaking the user will see options for:
228 - Copying / Pasting (All BASIN types)
229 - Creating an Alias (All BASIN types)
230 - Deleting (All BASIN types)
231 - Creating an appropriate blank sub-object
232 - Exporting (Regions only)
233 - Adding it to the Dock Widget (All BASIN types,
234 though there is no garauntee the dock will do
235 anything useful with it)
236 """
237
238 if (not self._bridge._connected) or (not self._bridge._parallel):
239 return
240 item = self.getSingleSelectedItem ()
241 if item == None:
242 menu = QMenu ("Right-Click", self._bridge._mainWindow)
243 menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/createregion.png")), QString ("Create Region"),
244 self.CreateRegion)
245 menu.exec_ (position)
246 return
247
248 menu = QMenu ("Right-Click", self._bridge._mainWindow)
249 menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/copy.png")), QString ("Copy"), item.Copy)
250 if item.Type () != "ATTRIBUTE":
251 paste = menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/paste.png")), QString ("Paste"), item.Paste)
252 if not self._copy:
253 paste.setEnabled (False)
254 else:
255 if item.Type () == "REGION" and self._copy.Type () == "ATTRIBUTE":
256 paste.setEnabled (False)
257 elif (item.Type () == "GRID" or item.Type () == "LIST") and \
258 not self._copy.Type () == "ATTRIBUTE":
259 paste.setEnabled (False)
260 menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/delete.png")), QString ("Delete"), item.Delete)
261
262 if item.Type () == "REGION":
263 menu.addSeparator ()
264 menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/createregion.png")),
265 QString ("Create Region"),
266 item.CreateRegion)
267 menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/creategrid.png")),
268 QString ("Create Grid"),
269 item.CreateGrid)
270 menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/createlist.png")),
271 QString ("Create List"),
272 item.CreateList)
273
274 menu.addSeparator ()
275
276 menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/alias.png")), QString ("Create Alias..."),
277 self._bridge._varViewer.createAlias)
278
279 if item.Type () == "ATTRIBUTE":
280 menu.addAction ("Convert to list...", self.AttributeToList)
281
282 menu.exec_ (position)
283
285 """
286 """
287
288 item = self.getSingleSelectedItem ()
289 if item == None:
290 return
291
292 dialog = Actions.NameDialog (self._bridge._mainWindow, "Convert To List...", QIcon (Constants.ICON_DEFAULT))
293 result = dialog.result ()
294 if result == 1 and dialog.name.text () != "":
295 self._bridge._ipyShell.Command (str (dialog.name.text ()) + " = " + \
296 str (item._shellAccess) + ".to_list ()", True)
297
299 """
300 Overview
301 ========
302 This method will potentially create a top-level Region. It will call a dialog which will request
303 a name from the user, or they can cancel. The given name will become the name of the new top-
304 level Region, or nothing will happen given a blank name / cancel.
305 """
306
307 item = self.getSingleSelectedItem ()
308
309 if item == None:
310 dialog = NameDialog (self._bridge._mainWindow, "Top Region", QIcon (Constants.ICON_DIR + \
311 "/misc/createregion.png"))
312 result = dialog.result ()
313 if result == 1 and dialog.name.text () != "":
314 commands = []
315 commands.append (str (dialog.name.text ()) + " = Region ()")
316 commands.append (str (dialog.name.text ()) +
317 ".set_name (\"" + str (dialog.name.text ()) + "\")")
318 self._bridge._ipyShell.BatchCommands (commands, True)
319
321 """
322 Overview
323 ========
324 This initial drag event merely checks to make sure that the source of the drag came from the
325 BASIN_variable_viewer.List object.
326 """
327
328 if ev.source () == self._bridge._varViewer:
329 ev.accept ()
330 else:
331 ev.ignore ()
332
334 """
335 Overview
336 ========
337 This midway drag move event merely checks to make sure that the source of the drag came from the
338 BASIN_variable_viewer.List object.
339 """
340
341 if ev.source () == self._bridge._varViewer:
342 ev.accept ()
343 else:
344 ev.ignore ()
345
347 """
348 Overview
349 ========
350 This method will determine the type of free variable being dropped on the RegionTree, and
351 the exact object type it was dropped on.
352 """
353
354 if ev.source () == self._bridge._varViewer:
355
356 source = self._bridge.PopDrugItem ()
357
358 target = self.itemAt (ev.pos ())
359
360
361 if target == None:
362 ev.ignore ()
363 return
364
365 if source.Type () != "VALUE" and source._alias != None:
366 self._bridge._statusBar.showTimed ("Can't add an alias, it is already " + \
367 "coupled to another item!", 2.5)
368 return
369
370 if source.Type () == "ATTRIBUTE" or (source.Type () == "VALUE" and \
371 source._type == "_basin.Attribute"):
372 if target.Type () == "LIST" or target.Type () == "GRID":
373 self._appendAttributeFromFreeVar (source._shellAccess, target._shellAccess)
374 else:
375 self._bridge._statusBar.showTimed ("Attributes may only be appended to Lists or Grids", 2.5)
376 elif source.Type () == "GRID" or (source.Type () == "VALUE" and \
377 source._type == "_basin.Grid"):
378 if target.Type () == "REGION":
379 self._appendGridFromFreeVar (source._shellAccess, target._shellAccess)
380 else:
381 self._bridge._statusBar.showTimed ("Grids may only be appended to Regions", 2.5)
382 elif source.Type () == "LIST" or (source.Type () == "VALUE" and \
383 source._type == "_basin.List"):
384 if target.Type () == "REGION":
385 self._appendListFromFreeVar (source._shellAccess, target._shellAccess)
386 else:
387 self._bridge._statusBar.showTimed ("Lists may only be appended to Regions", 2.5)
388 elif source.Type () == "REGION" or (source.Type () == "VALUE" and \
389 source._type == "_basin.Region"):
390 if target.Type () == "REGION":
391 self._appendRegionFromFreeVar (source._shellAccess, target._shellAccess)
392 else:
393 self._bridge._statusBar.showTimed ("Regions may only be appended to Regions or BASIN", 2.5)
394 else:
395 self._bridge._statusBar.showTimed ("Only BASIN objects may be appended to the Region Viewer", 2.5)
396
397 ev.setDropAction (Qt.LinkAction)
398 ev.accept ()
399 else:
400 ev.ignore ()
401
403 """
404 Overview
405 ========
406 This will occur when a user drags a free variable BASIN Attribute and drops it onto
407 a RegionTree object that is capable of being its parent, in this case Lists or Grids.
408
409 To accomplish this, it can use the internal `basin_kernel` command to add the attribute.
410 Also, the GUI will need to be updated that this has made. This is because we aren't
411 destroying the free floating attribute, but it just turns into an alias for the object
412 you are adGui ding into the RegionTree.
413 """
414
415 commands = []
416 commands.append (target + ".add_attribute (\"" + source +
417 "\", " + source + ")")
418 self._bridge._ipyShell.replaceFreeVariable (source, target + ".get_attribute (\"" +
419 source + "\")")
420 self._bridge._ipyShell.BatchCommands (commands, True)
421
423 """
424 Overview
425 ========
426 This will occur when a user drags a free variable BASIN Grid and drops it onto a
427 RegionTree object that is capable of being its parent, in this case Regions.
428
429 To accomplish this, it can use the internal `basin_kernel` command to add the Grid.
430 Also, the GUI will need to be updated that this has made. This is because we aren't
431 destroying the free floating Grid, but it just turns into an alias for the object
432 you are adding into the RegionTree.
433 """
434
435 commands = []
436 commands.append (target + ".add_grid (\"" + source + "\", " +
437 source + ")")
438 self._bridge._ipyShell.replaceFreeVariable (source, target + ".get_grid (\"" +
439 source + "\")")
440 self._bridge._ipyShell.BatchCommands (commands, True)
441
443 """
444 Overview
445 ========
446 This will occur when a user drags a free variable BASIN List and drops it onto a
447 RegionTree object that is capable of being its parent, in this case Regions.
448
449 To accomplish this, it can use the internal `basin_kernel` command to add the List.
450 Also, the GUI will need to be updated that this has made. This is because we aren't
451 destroying the free floating List, but it just turns into an alias for the object you
452 are adding into the RegionTree.
453 """
454
455 commands = []
456 commands.append (target + ".add_list (\"" + source +
457 "\", " + source + ")")
458 self._bridge._ipyShell.replaceFreeVariable (source, target + ".get_list (\"" +
459 source + "\")")
460 self._bridge._ipyShell.BatchCommands (commands, True)
461
463 """
464 Overview
465 ========
466 This will occur when a user drags a free variable BASIN Region and drops it onto a
467 RegionTree object that is capable of being its parent, in this case Regions.
468
469 To accomplish this, it can use the internal `basin_kernel` command to add the Region.
470 Also, the GUI will need to be updated that this has made. This is because we aren't
471 destroying the free floating Region, but it just turns into an alias for the object
472 you are adding into the RegionTree.
473 """
474
475 commands = []
476 commands.append (target + ".add_child (" + source + ")")
477 self._bridge._ipyShell.BatchCommands (commands, True)
478
479
481 """
482 Overview
483 ========
484 This class graphically keeps track of all free floating variables within the python session.
485 Note, it keeps track of variables in the parallel session, or commands entered while in
486 %autopx mode. There are essentially two types of free floating variables that are represented:
487 - Aliases: Variables with point to structured Basin objects. Note that not all basin objects
488 are aliases. An attribute without a list, for instance, isn't structured.
489 - Values: Self contained variables that are not references to a Basin object.
490 """
492 """
493 """
494 QTreeWidget.__init__ (self)
495 self.setAcceptDrops (True)
496
497 self._bridge = bridge
498 bridge.setVariableViewer (self)
499
500 headers = QStringList ("Variables")
501 headers.append ("Type")
502 headers.append ("Reference")
503 self.setHeaderLabels (headers)
504 self.setDragEnabled (True)
505 self.setAcceptDrops (True)
506
507 self.makePython ()
508
509 box = QGroupBox ("Free Python Variables")
510 boxLayout = QHBoxLayout (box)
511 boxLayout.addWidget (self)
512 layout.addWidget (box)
513
515 freeVars = freeInfo[0]
516 freeAccess = freeInfo[1]
517 self.clear ()
518 self.makePython ()
519 if len (freeVars) == 0 or len (freeAccess) == 0:
520 return
521 for index in xrange (len (freeVars)):
522 if freeVars.count (freeVars[index]) < 2:
523 result = self.findReference (freeAccess[index])
524 if result == False:
525 Items.Value (self.top, freeVars[index])
526 else:
527 Items.Alias (self.top, str (freeVars[index]), str (freeAccess[index]))
528 self.expandItem (self.top)
529 self.resizeColumnToContents (2)
530 self.resizeColumnToContents (1)
531 self.resizeColumnToContents (0)
532
534 self.top = QTreeWidgetItem ()
535 self.top.setText (0, "Python")
536 self.top.setFlags (Qt.ItemIsDropEnabled)
537 self.addTopLevelItem (self.top)
538
540 result = self._bridge._ipyShell.SilentCommand ("print \"\\n\" + str (" + name + ")")
541 if result.find ("Error") != -1:
542 self._bridge.Error ("BASIN_variable_viewer.py", "VariableViewer",
543 "isBasinObject", result)
544 return False
545 result = result.split ("\n")[3]
546 if re.compile ("<_basin.*>").match (result):
547 return True
548 return False
549
551 iter = QTreeWidgetItemIterator (self._bridge._regionViewer)
552 iter += 1
553 while iter.value ():
554 if shellAccess.replace (" ", "") == \
555 iter.value ()._shellAccess.replace (" ", ""):
556 return True
557 iter += 1
558 return False
559
561 if attr == None:
562 attr = self._bridge._regionViewer.getSingleSelectedItem ()
563 if attr == None:
564 return
565 dialog = QDialog ()
566 dialog.setWindowTitle (QString ("Create Alias..."))
567 dialog.setWindowIcon (QIcon (QString (Constants.ICON_DIR + "/alias.png")))
568 layout = QVBoxLayout (dialog)
569 topLayout = Constants.BQHBoxLayout (layout)
570 bottomLayout = Constants.BQHBoxLayout (layout)
571
572 name = QLineEdit (attr._name)
573 regexp = QRegExp ("[A-Za-z_][0-9A-Za-z_]*")
574 valid = QRegExpValidator (regexp, name)
575 name.setValidator (valid)
576 topLayout.addWidget (QLabel (QString ("Alias Name:")))
577 topLayout.addWidget (name)
578
579 acceptButton = Constants.BQPushButton ("Ok", bottomLayout)
580 QObject.connect (acceptButton, SIGNAL ("clicked ()"), dialog.accept)
581 rejectButton = Constants.BQPushButton ("Cancel", bottomLayout)
582 QObject.connect (rejectButton, SIGNAL ("clicked ()"), dialog.reject)
583
584 self._bridge.CursorNorm ()
585 result = dialog.exec_ ()
586
587 if result == 1:
588 result = self._bridge._ipyShell.Command (str (name.text ()) + " = " +
589 str (attr._shellAccess))
590 if result.find ("Error") != -1:
591 self._bridge.Error ("BASIN_variable_viewer.py", "VariableViewer",
592 "createAlias", result)
593
595 items = self.selectedItems ()
596 if len (items) == 1:
597 return items[0]
598 return None
599
601 if ev.source () == self._bridge._regionViewer:
602 self.createAlias (self._bridge.PopDrugItem ())
603
604 ev.setDropAction (Qt.LinkAction)
605 ev.accept ()
606 else:
607 ev.ignore ()
608
610 if ev.source () == self._bridge._regionViewer:
611 ev.accept ()
612 else:
613 ev.ignore ()
614
616 if ev.source () == self._bridge._regionViewer:
617 ev.accept ()
618 else:
619 ev.ignore ()
620
624
626 if (ev.globalPos () - self._dragStartPosition).manhattanLength () > \
627 QApplication.startDragDistance ():
628 object = self.getSingleSelectedItem ()
629 if object != None:
630 mime = QMimeData ()
631 mime.setData (QString ("text/plain"), "")
632 drag = QDrag (self)
633 drag.setMimeData (mime)
634 if object.Type () == "ALIAS":
635 self._bridge._drugItem = object
636
637 elif object.Type () == "VALUE":
638 self._bridge._drugItem = object
639 dropAction = drag.start (Qt.LinkAction)
640
645
650
652 item = self.getSingleSelectedItem ()
653 if item == None:
654 return
655
656 menu = QMenu ("Right-Click", self._bridge._mainWindow)
657 if item.Type () == "ALIAS":
658 if item.Type () == "REGION":
659 menu.addAction (self._bridge._actions.fExport)
660 elif item.Type () == "VALUE":
661 item = item
662
663 menu.addAction (QIcon (QString (Constants.ICON_DIR + "/misc/delete.png")), QString ("Delete"), item.Delete)
664
665 menu.exec_ (position)
666