hypview.py 19.9 KB
Newer Older
Russ Fish's avatar
Russ Fish committed
1 2 3 4
#! /usr/bin/env python
#
# hypview - HyperViewer application.
# For description of script args, invoke with any dash arg or see the "usage:" message below.
Russ Fish's avatar
Russ Fish committed
5
#
Russ Fish's avatar
Russ Fish committed
6
# Copyright (c) 2004 University of Utah and the Flux Group.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
Russ Fish's avatar
Russ Fish committed
26
#
Russ Fish's avatar
Russ Fish committed
27 28 29 30 31

##import pdb

import string
import sys
Russ Fish's avatar
Russ Fish committed
32
import types
Russ Fish's avatar
Russ Fish committed
33
import os
Russ Fish's avatar
Russ Fish committed
34 35

import hv
36 37 38
# Constants from HypData.h
HV_ANIMATE = 0

Russ Fish's avatar
Russ Fish committed
39 40 41 42 43
import exptToHv
from hvFrameUI import *
from OpenGL.GL import * 

from wxPython.wx import *
44
from wxPython.glcanvas import wxGLCanvas
Russ Fish's avatar
Russ Fish committed
45

Russ Fish's avatar
Russ Fish committed
46
# A wxPython application object.
Russ Fish's avatar
Russ Fish committed
47 48 49 50 51 52
class hvApp(wxApp):
    
    ##
    # The app initialization.
    def OnInit(self):
	
53 54 55 56
	filename = filearg = default_project = project = experiment = root = None
	if os.environ.has_key("EMULAB_PROJECT"):    # Default project name to use.
	    default_project = project = os.environ["EMULAB_PROJECT"]

Russ Fish's avatar
Russ Fish committed
57
	# Any dash argument prints a usage message and exits.
58
	if len(sys.argv) == 2 and sys.argv[1][0] == '-': 
Russ Fish's avatar
Russ Fish committed
59
	    print '''Hyperviewer usage:
Russ Fish's avatar
Russ Fish committed
60
  No args - Starts up the GUI.	Use the File/Open menu item to read a topology.
Russ Fish's avatar
Russ Fish committed
61
  One arg - A .hyp file name.  Read it in and start the GUI, e.g.:
62
      ./hypview BigLan.hyp
Russ Fish's avatar
Russ Fish committed
63 64
  Two args - Project and experiment names in the database.
      Get the topology from XMLRPC, make a .hyp file, start as above.
65
      ./hypview testbed BigLan
Russ Fish's avatar
Russ Fish committed
66
  Three args - Project and experiment names, plus an optional root node name.
67
      ./hypview testbed BigLan clan'''
Russ Fish's avatar
Russ Fish committed
68
	    sys.exit()
Russ Fish's avatar
Russ Fish committed
69 70
	    pass
	
71
	# Given command-line argument(s), attempt to read in a topology.
Russ Fish's avatar
Russ Fish committed
72 73
	# One command-line argument: read from a .hyp file.
	# (File/experiment input is also in the OnOpenFile and OnOpenExperiment methods.)
74
	elif len(sys.argv) == 2:
75
	    filename = filearg = sys.argv[1]
76
	    print "Reading file:", filename
Russ Fish's avatar
Russ Fish committed
77
	    pass
Russ Fish's avatar
Russ Fish committed
78 79
	
	# Two args: read an experiment from the DB via XML-RPC, and make a .hyp file.
80
	elif len(sys.argv) == 3:
Russ Fish's avatar
Russ Fish committed
81
	    project = sys.argv[1]
82 83
	    if project == "":
		project = default_project
Russ Fish's avatar
Russ Fish committed
84
	    experiment = sys.argv[2]
85 86
	    print "Getting project:", project + ", experiment:", experiment
	    filename = exptToHv.getExperiment(project, experiment)
Russ Fish's avatar
Russ Fish committed
87
	    pass
88

Russ Fish's avatar
Russ Fish committed
89
	# Three args: experiment from database, with optional graph root node.
90
	elif len(sys.argv) == 4:
Russ Fish's avatar
Russ Fish committed
91
	    project = sys.argv[1]
92 93
	    if project == "":
		project = default_project
Russ Fish's avatar
Russ Fish committed
94 95
	    experiment = sys.argv[2]
	    root = sys.argv[3]
96 97 98
	    print "Getting project:", project + ", experiment:", experiment \
		  + ", root node:", root
	    filename = exptToHv.getExperiment(project, experiment, root)
Russ Fish's avatar
Russ Fish committed
99 100 101 102 103 104 105 106
	    pass

	self.frame = hvFrame(None, -1, "wxHyperViewer", (100,0), (750,750))
	self.frame.app = self	# A back-reference here from the frame.
	self.openDialog = hvOpen(None, -1, "Open HyperViewer Data")
	self.openDialog.app = self
	self.usageDialog = UsageDialogUI(None, -1, "HyperViewer Usage")
	
107 108 109 110

	# Initialize the text fields in the File/Open dialog.
	if filearg:
	    self.openDialog.FileToOpen.SetValue(filearg) 
111
        self.openDialog.LoginName.SetValue(exptToHv.login_id)
112 113
	if project:
	    self.openDialog.ProjectName.SetValue(project)
114 115
	elif default_project:
	    self.openDialog.ProjectName.SetValue(default_project)
116 117 118 119 120 121
	if experiment:
	    self.openDialog.ExperimentName.SetValue(experiment)
	    self.openDialog.ExperimentName.SetFocus()
	if root:
	    self.openDialog.ExperimentRoot.SetValue(root)
	
122
	# Make the top-level window visible.
Russ Fish's avatar
Russ Fish committed
123 124
	self.frame.Show()
	self.SetTopWindow(self.frame)
125

Russ Fish's avatar
Russ Fish committed
126 127 128 129 130 131 132 133 134 135 136 137
	if filename:
	    if type(filename) is types.ListType:
		print "Failed to read experiment from database."
		exptError = '%s %s\n%s' % tuple(filename[1:4])
		print exptError
		self.frame.shutdown()
	    else:
		if not self.frame.ReadTopFile("wxHyperViewer", filename):
		    #print "Could not open ", filename # Already printed error in C++.
		    self.frame.shutdown() 
		pass
	    pass
138

Russ Fish's avatar
Russ Fish committed
139
	return True			# OnInit success.
Russ Fish's avatar
Russ Fish committed
140 141
    pass

Russ Fish's avatar
Russ Fish committed
142 143
# hvFrame - The semantics (methods) of the UI of the application.  Notice that this object
# inherits from hvFrameUI in hvFrameUI.py, which is generated by wxGlade from hvgui.wxg .
Russ Fish's avatar
Russ Fish committed
144 145 146 147 148 149 150
class hvFrame(hvFrameUI):
    
    ##
    # Frame initialization.
    def __init__(self, *args, **kwds):
	# Set up the wxGlade part.
	hvFrameUI.__init__(self, *args, **kwds)
Russ Fish's avatar
-  
Russ Fish committed
151 152 153

	# Remember the original width of the controls panel in the splitter.
	self.controlsWidth = self.window_1.GetSize().width - self.window_1.GetSashPosition()
Russ Fish's avatar
Russ Fish committed
154
	
Russ Fish's avatar
-  
Russ Fish committed
155
	self.vwr = None		# Load data and create the viewer later in ReadTopFile.
Russ Fish's avatar
Russ Fish committed
156
	self.currNode = None	# Nothing selected at first.
Russ Fish's avatar
Russ Fish committed
157 158

	# Control events.  (HyperViewer events are connected after loading data.)
159 160 161
	# EVT_ handler-setting functions are defined in site-packages/wxPython/wx.py .
	# See http://www.wxwindows.org/manuals/2.4.2/wx470.htm#eventhandlingoverview .
	EVT_TEXT_ENTER(self.NodeName, -1, self.OnNodeName)
Russ Fish's avatar
Russ Fish committed
162 163 164 165 166 167 168 169
	EVT_CHECKBOX(self.DrawSphere, -1, self.OnDrawSphere)
	EVT_CHECKBOX(self.DrawNodes, -1, self.OnDrawNodes)
	EVT_CHECKBOX(self.DrawLinks, -1, self.OnDrawLinks)
	EVT_CHECKBOX(self.KeepAspect, -1, self.OnKeepAspect)
	EVT_CHECKBOX(self.LabelToRight, -1, self.OnLabelToRight)
	EVT_BUTTON(self.GoToTop, -1, self.OnGoToTop)
	EVT_BUTTON(self.ShowLinksIn, -1, self.OnShowLinksIn)
	EVT_BUTTON(self.HideLinksIn, -1, self.OnHideLinksIn)
Russ Fish's avatar
Russ Fish committed
170
	EVT_CHECKBOX(self.DescendLinksIn, -1, self.OnDescendLinksIn)
Russ Fish's avatar
Russ Fish committed
171 172
	EVT_BUTTON(self.ShowLinksOut, -1, self.OnShowLinksOut)
	EVT_BUTTON(self.HideLinksOut, -1, self.OnHideLinksOut)
Russ Fish's avatar
Russ Fish committed
173
	EVT_CHECKBOX(self.DescendLinksOut, -1, self.OnDescendLinksOut)
Russ Fish's avatar
Russ Fish committed
174
	EVT_BUTTON(self.HelpButton, -1, self.OnUsage)
175
	EVT_CHOICE(self.LabelsMode, -1, self.OnLabelsMode)
Russ Fish's avatar
Russ Fish committed
176 177 178 179
	EVT_MENU(self, 1, self.OnOpen)
	EVT_MENU(self, 2, self.OnExit)
	EVT_MENU(self, 3, self.OnUsage)
	EVT_SPINCTRL(self.CountGenNode, -1, self.OnCountGenNode)
180
	EVT_CHAR(self.CountGenNode, self.OnCountGenNode_CHAR)
Russ Fish's avatar
Russ Fish committed
181
	EVT_SPINCTRL(self.CountGenLink, -1, self.OnCountGenLink)
182
	EVT_CHAR(self.CountGenLink, self.OnCountGenLink_CHAR)
Russ Fish's avatar
Russ Fish committed
183 184 185
	EVT_SCROLL_ENDSCROLL(self.AnimStepCount, self.OnAnimStepCount)	  # Windows
	EVT_SLIDER(self.AnimStepCount, -1, self.OnAnimStepCount)	  # GTK
	
186 187 188 189 190 191 192 193
	# Mouse-generated events.
	EVT_LEFT_DOWN(self.hypView, self.OnClick)
	EVT_LEFT_UP(self.hypView, self.OnClick)
	EVT_MIDDLE_DOWN(self.hypView, self.OnClick)
	EVT_MIDDLE_UP(self.hypView, self.OnClick)
	EVT_MOTION(self.hypView, self.OnMove)
	EVT_SIZE(self.hypView, self.OnResizeCanvas)

Russ Fish's avatar
Russ Fish committed
194
	# Other events.
Russ Fish's avatar
-  
Russ Fish committed
195 196
	EVT_SIZE(self.window_1, self.OnResizeWindow)
	EVT_SPLITTER_SASH_POS_CHANGED(self.window_1, -1, self.OnSashChanged)
Russ Fish's avatar
Russ Fish committed
197 198
	EVT_CLOSE(self, self.OnExit)
	# These do nothing until the vwr is instantiated below.
Russ Fish's avatar
Russ Fish committed
199 200 201 202 203 204
	EVT_IDLE(self.hypView, self.OnIdle)
	EVT_PAINT(self.hypView, self.OnPaint)
	
	pass
    
    ##
Russ Fish's avatar
Russ Fish committed
205 206 207 208 209 210 211 212 213 214 215 216
    # Close the top-level windows, so the MainLoop exits.
    def shutdown(self):
	EVT_CLOSE(self, None)		# Prevent infinite loop.
	self.app.openDialog.Close(True)
	self.app.usageDialog.Close(True)
	self.Close(True)
	self.app.ExitMainLoop()
	pass
    
    ##
    # Read in a topology file and instantiate the C++ Hyperviewer object.
    # Returns True on success, False on failure.
Russ Fish's avatar
Russ Fish committed
217 218 219
    def ReadTopFile(self, name, file):
	self.SetTitle(name + " " + file)
	
Russ Fish's avatar
Russ Fish committed
220 221 222
	# Select the OpenGL Graphics Context from the wxGLCanvas in the hvFrameUI.
	self.hypView.SetCurrent()   

223
	# Get window info from the wxWindow base class of the wxGLCanvas object.
Russ Fish's avatar
Russ Fish committed
224 225 226 227 228 229 230 231 232
	if os.name == 'nt':
	    # GetHandler returns the platform-specific handle of the physical window:
	    # HWND for Windows, Widget for Motif or GtkWidget for GTK.
	    window = self.hypView.GetHandle()
	else:
	    # The wxGLCanvas has the graphics context and does the SwapBuffers for us on GTK.
	    window = self.hypView.this
	    pass
	##print "self.hypView", self.hypView, "window", window
233 234
	# GetSizeTuple is the wxPython version of GetSize.  Returns size of the
	# entire window in pixels, including title bar, border, scrollbars, etc.
Russ Fish's avatar
Russ Fish committed
235
	width, height = self.hypView.GetSizeTuple()
236 237

	# Instantiate and initialize the SWIG'ed C++ HypView object, loading graph data.
238
	if self.vwr is not None:
Russ Fish's avatar
Russ Fish committed
239
	    ### Give up on hvReadFile; the cleanup logic is busted.  Always make a new HypView.
240
	    ##print "hvkill", self.vwr
Russ Fish's avatar
Russ Fish committed
241
	    ##hv.hvKill(self.vwr)  ## And don't even clean up the old one.
242

Russ Fish's avatar
Russ Fish committed
243 244
	    # This is *really* evil... It still fails every other time, so do it twice.  :-(
	    self.vwr = hv.hvMain([str(name), str(file)], # Must be non-unicode strings.
Russ Fish's avatar
Russ Fish committed
245
	                         window, width, height)  # Win32 needs the window info.
Russ Fish's avatar
Russ Fish committed
246
	    ##hv.hvKill(self.vwr)
247 248
	    self.vwr = None

Russ Fish's avatar
Russ Fish committed
249
	self.vwr = hv.hvMain([str(name), str(file)], # Must be non-unicode strings.
Russ Fish's avatar
Russ Fish committed
250
			     window, width, height)  # Win32 needs the window info.
Russ Fish's avatar
Russ Fish committed
251
	if self.vwr is None:			     # Must have been a problem....
Russ Fish's avatar
Russ Fish committed
252 253
	    return False

Russ Fish's avatar
Russ Fish committed
254
	self.OnGoToTop(None)			     # Show info for the top node.
Russ Fish's avatar
Russ Fish committed
255
	
Russ Fish's avatar
Russ Fish committed
256
	return True
Russ Fish's avatar
Russ Fish committed
257 258 259 260 261 262
    
    ##
    # Draw the OpenGL content and make it visible.
    def DrawGL(self):
	##print "in DrawGL"
	self.vwr.drawFrame()
Russ Fish's avatar
-  
Russ Fish committed
263
	pass
Russ Fish's avatar
Russ Fish committed
264 265 266 267 268 269
    
    ##
    # The GUI displays information about the currently selected node.
    def SelectedNode(self, node):
	##print node
	if string.find(node, "|") == -1:	# Links are "node1|node2".
Russ Fish's avatar
Russ Fish committed
270
	    self.currNode = node
Russ Fish's avatar
Russ Fish committed
271 272 273
	    self.NodeName.Clear()
	    self.NodeName.WriteText(node)
	    self.ChildCount.SetLabel(str(self.vwr.getChildCount(node)))
274 275

	    linksIn = self.vwr.getIncomingCount(node)
Russ Fish's avatar
Russ Fish committed
276
	    self.LabelLinksIn.SetLabel(
277
		"Non-tree Links in: " + str(linksIn))
Russ Fish's avatar
Russ Fish committed
278
	    self.OnDescendLinksIn(None)
279 280

	    linksOut = self.vwr.getOutgoingCount(node)
Russ Fish's avatar
Russ Fish committed
281
	    self.LabelLinksOut.SetLabel(
282
		"Non-tree Links out: " + str(linksOut))
Russ Fish's avatar
Russ Fish committed
283
	    self.OnDescendLinksOut(None)
Russ Fish's avatar
Russ Fish committed
284 285 286 287
	pass
    
    ##
    # Event handling for the HyperViewer canvas.
288 289 290 291 292 293
    # Go to a node when its name is typed and Enter is pressed.
    def OnNodeName(self, cmdEvent):
	if self.vwr is None:
	    return
	node = self.NodeName.GetValue()
	self.vwr.gotoNode(node, HV_ANIMATE)
294 295 296

	# Simulate picking the node as well.
	hv.selectCB(node, 0, 0);
297
	self.SelectedNode(node)
298 299 300

	self.DrawGL()
	pass
Russ Fish's avatar
Russ Fish committed
301 302
    # Check boxes control boolean state.
    def OnDrawSphere(self, cmdEvent):
303 304
	if self.vwr is None:
	    return
Russ Fish's avatar
Russ Fish committed
305 306
	self.vwr.setSphere(self.DrawSphere.IsChecked())
	self.DrawGL()
307
	pass
Russ Fish's avatar
Russ Fish committed
308
    def OnDrawNodes(self, cmdEvent):
309 310
	if self.vwr is None:
	    return
Russ Fish's avatar
Russ Fish committed
311 312
	self.vwr.setDrawNodes(self.DrawNodes.IsChecked())
	self.DrawGL()
313
	pass
Russ Fish's avatar
Russ Fish committed
314
    def OnDrawLinks(self, cmdEvent):
315 316
	if self.vwr is None:
	    return
Russ Fish's avatar
Russ Fish committed
317 318
	self.vwr.setDrawLinks(self.DrawLinks.IsChecked())
	self.DrawGL()
319
	pass
Russ Fish's avatar
Russ Fish committed
320
    def OnKeepAspect(self, cmdEvent):
321 322
	if self.vwr is None:
	    return
Russ Fish's avatar
Russ Fish committed
323 324
	self.vwr.setKeepAspect(self.KeepAspect.IsChecked())
	self.DrawGL()
325
	pass
Russ Fish's avatar
Russ Fish committed
326
    def OnLabelToRight(self, cmdEvent):
327 328
	if self.vwr is None:
	    return
Russ Fish's avatar
Russ Fish committed
329 330
	self.vwr.setLabelToRight(self.LabelToRight.IsChecked())
	self.DrawGL()
331
	pass
Russ Fish's avatar
Russ Fish committed
332 333 334 335
    
    ##
    # Buttons issue commands.
    def OnGoToTop(self, buttonEvent):
336 337 338 339 340 341
	if self.vwr is None:
	    return

	# Tell the HypView to reset.
	self.vwr.gotoCenterNode(HV_ANIMATE)

342
	# Simulate picking the top node.
343 344
	##ctr = self.vwr.getGraphCenter()
	ctr = hv.getGraphCenter()
345 346 347 348
	##print "center node", ctr
	hv.selectCB(ctr, 0, 0);

	# Update the node info and draw.
349
	self.SelectedNode(ctr)
Russ Fish's avatar
Russ Fish committed
350
	self.DrawGL()
351 352
	pass

Russ Fish's avatar
Russ Fish committed
353 354 355
    def OnShowLinksIn(self, buttonEvent):
	self.vwr.setDrawBackTo(hv.getSelected(), 1, self.DescendLinksIn.IsChecked())
	self.DrawGL()
356
	pass
Russ Fish's avatar
Russ Fish committed
357 358 359
    def OnHideLinksIn(self, buttonEvent):
	self.vwr.setDrawBackTo(hv.getSelected(), 0, self.DescendLinksIn.IsChecked())
	self.DrawGL()
360
	pass
Russ Fish's avatar
Russ Fish committed
361 362 363
    def OnShowLinksOut(self, buttonEvent):
	self.vwr.setDrawBackFrom(hv.getSelected(), 1, self.DescendLinksOut.IsChecked())
	self.DrawGL()
364
	pass
Russ Fish's avatar
Russ Fish committed
365 366 367
    def OnHideLinksOut(self, buttonEvent):
	self.vwr.setDrawBackFrom(hv.getSelected(), 0, self.DescendLinksOut.IsChecked())
	self.DrawGL()
368
	pass
Russ Fish's avatar
Russ Fish committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
    def OnDescendLinksIn(self, cmdEvent):
	node = self.currNode
	if node is None:
	    node = hv.getGraphCenter()
	linksIn = self.vwr.getIncomingCount(node)
	descend = self.DescendLinksIn.IsChecked()
	self.ShowLinksIn.Enable(descend or linksIn > 0)
	self.HideLinksIn.Enable(descend or linksIn > 0)
	self.DescendLinksIn.Enable(True)
	pass
    def OnDescendLinksOut(self, cmdEvent):
	node = self.currNode
	if node is None:
	    node = hv.getGraphCenter()
        linksOut = self.vwr.getOutgoingCount(node)
	descend = self.DescendLinksOut.IsChecked()
	self.ShowLinksOut.Enable(descend or linksOut > 0)
	self.HideLinksOut.Enable(descend or linksOut > 0)
	self.DescendLinksIn.Enable(True)
	pass
    
Russ Fish's avatar
Russ Fish committed
390 391 392
    ##
    # Combo boxes select between alternatives.
    def OnLabelsMode(self, cmdEvent):
393 394
	if self.vwr is None:
	    return
Russ Fish's avatar
Russ Fish committed
395 396 397 398 399 400 401 402 403 404
	which = self.LabelsMode.GetSelection()
	if which != -1:
	    self.vwr.setLabels(which)
	    self.DrawGL()
	    pass
	pass
    
    ##
    # Spin controls set integer state.
    def OnCountGenNode(self, spinEvent):
405 406 407
	##print "OnCountGenNode", self.vwr
	if self.vwr is None:
	    return
Russ Fish's avatar
Russ Fish committed
408 409
	self.vwr.setGenerationNodeLimit(self.CountGenNode.GetValue())
	self.DrawGL()
410
	pass
Russ Fish's avatar
Russ Fish committed
411
    def OnCountGenLink(self, spinEvent):
412 413 414
	##print "OnCountGenLink", self.vwr
	if self.vwr is None:
	    return
Russ Fish's avatar
Russ Fish committed
415 416
	self.vwr.setGenerationLinkLimit(self.CountGenLink.GetValue())
	self.DrawGL()
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
	pass

    ##
    # Actions for <ENTER> keypress in numeric fields.
    def OnCountGenNode_CHAR(self, keyEvent):
	key = keyEvent.GetKeyCode()
	if key == WXK_RETURN:
	    self.OnCountGenNode(keyEvent)
	    pass
	self.OnCharDigitsOnly(keyEvent)
	pass
    def OnCountGenLink_CHAR(self, keyEvent):
	key = keyEvent.GetKeyCode()
	if key == WXK_RETURN:
	    self.OnCountGenLink(keyEvent)
	    pass
	self.OnCharDigitsOnly(keyEvent)
	pass

    ##
    # Disable typing non-digits in numeric fields.
    def OnCharDigitsOnly(self, keyEvent):
	key = keyEvent.GetKeyCode()
	if ord('0') <= key <= ord('9') or key in (WXK_BACK, WXK_TAB) or key >= WXK_DELETE:
	    keyEvent.Skip()		# Skip actually means to process the event...
Russ Fish's avatar
Russ Fish committed
442 443
	    pass
	pass
444

Russ Fish's avatar
Russ Fish committed
445 446 447 448 449 450 451 452 453 454 455
    ##
    # Handle changes in the number of steps per second in animated moves.
    def OnAnimStepCount(self, scrollEvent):
	if self.vwr is None:
	    return
	# Convert from steps/second to fraction-of-a-second-per-step.
	stepsPerSec = self.AnimStepCount.GetValue()
	self.vwr.setGotoStepSize(1.0 / stepsPerSec)
        ##print "OnAnimStepCount", stepsPerSec, self.vwr.getGotoStepSize()
	pass
    
Russ Fish's avatar
Russ Fish committed
456 457 458
    ##
    # Menu items issue commands from the menu bar.
    def OnExit(self, cmdEvent):
Russ Fish's avatar
Russ Fish committed
459
	self.shutdown()
Russ Fish's avatar
Russ Fish committed
460 461 462 463 464 465 466 467
    def OnOpen(self, cmdEvent):
	self.app.openDialog.Show()
    def OnUsage(self, cmdEvent):
	self.app.usageDialog.Show()
    
    ##
    # Mouse click events.
    def OnClick(self, mouseEvent):
468 469 470
	if self.vwr is None:
	    return

471
	# Encode mouse button events for HypView.
Russ Fish's avatar
Russ Fish committed
472
	btnNum = -1
Russ Fish's avatar
Russ Fish committed
473 474
	
	# Left mouse button for X-Y motion of the hyperbolic center.
475
	if mouseEvent.LeftDown():
Russ Fish's avatar
Russ Fish committed
476 477 478 479 480 481
	    btnNum = 0
	    btnState = 0
	elif mouseEvent.LeftUp():
	    btnNum = 0
	    btnState = 1
	    pass
Russ Fish's avatar
Russ Fish committed
482 483
	
	# Middle button for rotation of the hyperbolic space.
Russ Fish's avatar
Russ Fish committed
484 485 486 487 488 489 490 491
	elif mouseEvent.MiddleDown():
	    btnNum = 1
	    btnState = 0
	elif mouseEvent.MiddleUp():
	    btnNum = 1
	    btnState = 1
	    pass
	
Russ Fish's avatar
Russ Fish committed
492 493 494 495 496
	# Left button with control or shift held down is also rotation.
	if btnNum == 0 and ( mouseEvent.ControlDown() or mouseEvent.ShiftDown() ):
	    btnNum = 1
	    pass
	
497
	# Handle mouse clicks in HypView.
Russ Fish's avatar
Russ Fish committed
498
	if btnNum != -1:
Russ Fish's avatar
Russ Fish committed
499
	    ##print "click", btnNum, btnState, mouseEvent.GetX(), mouseEvent.GetY()
Russ Fish's avatar
Russ Fish committed
500 501 502 503 504 505 506 507 508
	    self.vwr.mouse(btnNum, btnState, mouseEvent.GetX(), mouseEvent.GetY(), 0, 0)
	    self.vwr.redraw()
	    
	    # If a pick occurred, the current node name has changed.
	    self.SelectedNode(hv.getSelected())
	    pass
	pass
    
    ##
Russ Fish's avatar
-  
Russ Fish committed
509
    # Mouse motion events in the HyperViewer canvas.
Russ Fish's avatar
Russ Fish committed
510
    def OnMove(self, mouseEvent):
511 512 513
	if self.vwr is None:
	    return

Russ Fish's avatar
Russ Fish committed
514 515 516 517 518
	# Hyperviewer calls motion when a mouse button is clicked "active"
	if mouseEvent.LeftIsDown() or mouseEvent.MiddleIsDown():
	    self.vwr.motion(mouseEvent.GetX(), mouseEvent.GetY(), 0, 0)
	else:
	    # "passive" mouse motion is when there is no button clicked.
Russ Fish's avatar
Russ Fish committed
519
	    ##print "passive", mouseEvent.GetX(), mouseEvent.GetY()
Russ Fish's avatar
Russ Fish committed
520 521 522 523 524
	    self.vwr.passive(mouseEvent.GetX(), mouseEvent.GetY(), 0, 0)
	    pass
	self.vwr.redraw()
	pass
    
Russ Fish's avatar
-  
Russ Fish committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
    ##
    # Resizing for the splitter window.
    def OnResizeWindow(self, sizeEvent):
	# Keep the width of the controls panel in the splitter constant.
	self.window_1.SetSashPosition(self.window_1.GetSize().width - self.controlsWidth)
	pass

    def OnSashChanged(self, splitterEvent):
	# Remember an intentional change in the controls panel width.
	self.controlsWidth = self.window_1.GetSize().width - splitterEvent.GetSashPosition()
	pass

    ##
    # Resizing for the HyperViewer canvas.
    def OnResizeCanvas(self, sizeEvent):
	# Tie the size of the canvas to the panel it's in.
	size = self.panel_1.GetSize()
	# We get two resize events: one with a width of 20.  Ignore it.
	if size.width >= 20:
	    self.hypView.SetSize(size)

	    # Tell HyperViewer about the change.
547 548
	    if self.vwr:
		self.vwr.reshape(size.width, size.height)
Russ Fish's avatar
-  
Russ Fish committed
549 550
	pass
    
Russ Fish's avatar
Russ Fish committed
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
    ##
    # Other events generated by the event toploop and window system interface.
    def OnIdle(self, idleEvent):
	if self.vwr:
	    self.vwr.idle()
	    pass
	###idleEvent.RequestMore()
	pass
    
    def OnPaint(self, paintEvent):
	if self.vwr:
	    self.vwr.redraw()
	    pass
	pass
    pass

class hvOpen(OpenDialogUI):
    ##
    # Open dialog frame initialization.
    def __init__(self, *args, **kwds):
	# Set up the wxGlade part.
	OpenDialogUI.__init__(self, *args, **kwds)
	    
	EVT_BUTTON(self.OpenFile, -1, self.OnOpenFile)
575 576
	EVT_TEXT_ENTER(self.FileToOpen, -1, self.OnOpenFile)

Russ Fish's avatar
Russ Fish committed
577
	EVT_BUTTON(self.OpenExperiment, -1, self.OnOpenExperiment)
578
	EVT_TEXT_ENTER(self.LoginName, -1, self.OnLoginName)
579 580 581 582
	EVT_TEXT_ENTER(self.ProjectName, -1, self.OnOpenExperiment)
	EVT_TEXT_ENTER(self.ExperimentName, -1, self.OnOpenExperiment)
	EVT_TEXT_ENTER(self.ExperimentRoot, -1, self.OnOpenExperiment)

Russ Fish's avatar
Russ Fish committed
583 584 585 586 587
	pass
    
    ##
    # Get topology data from a file.
    def OnOpenFile(self, cmdEvent):
Russ Fish's avatar
Russ Fish committed
588
	file = self.FileToOpen.GetLineText(0)
589 590 591 592
	if file == "":
	    msg = "Enter a file path."
	else:
	    msg = 'Reading topology file "%s".' % file
Russ Fish's avatar
Russ Fish committed
593 594 595 596
	print msg
	self.FileMsg.SetLabel(msg)
	self.Refresh()
	self.Update()
597 598 599
	if file == "":
	    return
	
Russ Fish's avatar
Russ Fish committed
600 601 602 603 604 605
	if self.app.frame.ReadTopFile("wxHyperViewer", file):
	    self.Hide();		# Success.
	    self.FileMsg.SetLabel(" ")
	else:
	    fileError = "Could not open " + file
	    self.FileMsg.SetLabel(fileError)
Russ Fish's avatar
Russ Fish committed
606
	    pass
Russ Fish's avatar
Russ Fish committed
607 608
	pass
    
609 610 611 612 613 614
    ##
    # Change the SSH login name.
    def OnLoginName(self, cmdEvent):
        exptToHv.login_id = self.LoginName.GetLineText(0)
        pass
    
Russ Fish's avatar
Russ Fish committed
615 616 617 618 619 620
    ##
    # Get topology data for an experiment from the database via XML-RPC.
    def OnOpenExperiment(self, cmdEvent):
	project = self.ProjectName.GetLineText(0)
	experiment = self.ExperimentName.GetLineText(0)
	root = self.ExperimentRoot.GetLineText(0)
Russ Fish's avatar
Russ Fish committed
621

622 623 624 625
	if project == "" or experiment == "":
	    msg = "Enter a project name and experiment."
	else:
	    msg = 'Getting experiment %s/%s.' % (project, experiment)
Russ Fish's avatar
Russ Fish committed
626
	    pass
Russ Fish's avatar
Russ Fish committed
627 628 629 630
	print msg
	self.ExperimentMsg.SetLabel(msg)
	self.Refresh()
	self.Update()
631 632 633
	if project == "" or experiment == "":
	    return
	
Russ Fish's avatar
Russ Fish committed
634
	hypfile = exptToHv.getExperiment(project, experiment, root)
Russ Fish's avatar
Russ Fish committed
635 636 637 638 639 640 641 642 643 644 645 646 647
	if type(hypfile) is types.ListType:
	    exptError = '%s %s\n%s' % tuple(hypfile[1:4])
	    print exptError
	    self.ExperimentMsg.SetLabel(exptError)

	elif self.app.frame.ReadTopFile("wxHyperViewer", hypfile):
	    self.Hide();		# Success.
	    self.ExperimentMsg.SetLabel(" ")
	else:
	    fileError = "Could not open " + hypfile
	    print fileError
	    self.ExperimentMsg.SetLabel(fileError)
	    pass
Russ Fish's avatar
Russ Fish committed
648 649 650 651 652 653
	pass
    
    pass

app = hvApp(0)		# Create the wxPython application.
app.MainLoop()		# Handle events.