コード例 #1
0
ファイル: main_window.py プロジェクト: UoMCS/Perentie
	def __init__(self, system):
		"""
		A main-window which contains controls, error logs, and register & memory
		viewers.
		"""
		gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
		
		self.system = system
		
		# Number of log entries received while the log-viewer was collapsed
		self.unread_log_entries = 0
		
		# A list of peripheral viewer widgets
		self.periph_viewers = []
		
		# A list of extra memory-viewer windows
		self.memory_viewers = []
		
		# A list of extra register-viewer windows
		self.register_viewers = []
		
		# A flag which can be set to kill the refresh loop
		self.killed = False
		
		
		# Set the window icon
		self.set_icon_list(*about.get_icon_list())
		
		# General components
		self.control_bar = ControlBar(self.system)
		self.register_viewer = RegisterViewer(self.system)
		self.log_viewer = LogViewer(self.system)
		self.status_bar = StatusBar(self.system)
		
		# Two memory viewers, one disassembly view by default, one not
		self.memory_viewer_top = MemoryViewer(self.system, True)
		self.memory_viewer_btm = MemoryViewer(self.system, False)
		
		# Placeholder messages
		self._init_placeholders()
		
		# Set up keyboard shortcuts
		self.add_accel_group(self.control_bar.accelerators)
		
		# Propagate out refreshes when the device's state is changed
		self.control_bar.connect("device-state-changed", self._on_device_state_changed)
		self.register_viewer.connect("edited", self._on_device_state_changed)
		self.memory_viewer_top.connect("edited", self._on_device_state_changed)
		self.memory_viewer_btm.connect("edited", self._on_device_state_changed)
		
		# Calls from the control bar
		self.control_bar.connect("refresh-clicked", self._on_device_state_changed)
		self.control_bar.connect("select-target-clicked", self._on_select_target_clicked)
		self.control_bar.connect("quit-clicked", self._on_quit_clicked)
		self.control_bar.connect("new-memory-viewer-clicked", self._on_new_viewer_clicked,
		                         MemoryViewer, self.memory_viewers, "Memory Viewer")
		self.control_bar.connect("new-register-viewer-clicked", self._on_new_viewer_clicked,
		                         RegisterViewer, self.register_viewers, "Register Viewer")
		
		self._init_gui()
		self._init_adjustments()
		
		# A recurring call which is used to refresh the display
		glib.timeout_add(MainWindow.REFRESH_INTERVAL, self._on_interval)
		
		# Load up all architecture-specific stuff
		self._architecture_changed()
コード例 #2
0
ファイル: main_window.py プロジェクト: UoMCS/Perentie
class MainWindow(gtk.Window):
	
	__gsignals__ = {
		# Emitted when the window is closed and a new target is being requested
		"change-target": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()),
	}
	
	# Number of ms between screen refreshes
	REFRESH_INTERVAL = 300
	
	# Default window size on start
	DEFAULT_SIZE = (1024, 768)
	
	def __init__(self, system):
		"""
		A main-window which contains controls, error logs, and register & memory
		viewers.
		"""
		gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
		
		self.system = system
		
		# Number of log entries received while the log-viewer was collapsed
		self.unread_log_entries = 0
		
		# A list of peripheral viewer widgets
		self.periph_viewers = []
		
		# A list of extra memory-viewer windows
		self.memory_viewers = []
		
		# A list of extra register-viewer windows
		self.register_viewers = []
		
		# A flag which can be set to kill the refresh loop
		self.killed = False
		
		
		# Set the window icon
		self.set_icon_list(*about.get_icon_list())
		
		# General components
		self.control_bar = ControlBar(self.system)
		self.register_viewer = RegisterViewer(self.system)
		self.log_viewer = LogViewer(self.system)
		self.status_bar = StatusBar(self.system)
		
		# Two memory viewers, one disassembly view by default, one not
		self.memory_viewer_top = MemoryViewer(self.system, True)
		self.memory_viewer_btm = MemoryViewer(self.system, False)
		
		# Placeholder messages
		self._init_placeholders()
		
		# Set up keyboard shortcuts
		self.add_accel_group(self.control_bar.accelerators)
		
		# Propagate out refreshes when the device's state is changed
		self.control_bar.connect("device-state-changed", self._on_device_state_changed)
		self.register_viewer.connect("edited", self._on_device_state_changed)
		self.memory_viewer_top.connect("edited", self._on_device_state_changed)
		self.memory_viewer_btm.connect("edited", self._on_device_state_changed)
		
		# Calls from the control bar
		self.control_bar.connect("refresh-clicked", self._on_device_state_changed)
		self.control_bar.connect("select-target-clicked", self._on_select_target_clicked)
		self.control_bar.connect("quit-clicked", self._on_quit_clicked)
		self.control_bar.connect("new-memory-viewer-clicked", self._on_new_viewer_clicked,
		                         MemoryViewer, self.memory_viewers, "Memory Viewer")
		self.control_bar.connect("new-register-viewer-clicked", self._on_new_viewer_clicked,
		                         RegisterViewer, self.register_viewers, "Register Viewer")
		
		self._init_gui()
		self._init_adjustments()
		
		# A recurring call which is used to refresh the display
		glib.timeout_add(MainWindow.REFRESH_INTERVAL, self._on_interval)
		
		# Load up all architecture-specific stuff
		self._architecture_changed()
	
	
	def _init_placeholders(self):
		"""
		Create the placeholder messages which may be shown.
		"""
		self.unknown_device_placeholder = Placeholder(
			"Unknown Architecture",
			"The connected device's architecture is unknown. "+
			"Peripherals and general board functions may still " +
			"be used but register banks and memories cannot be " +
			"accessed.",
			gtk.STOCK_DIALOG_WARNING)
		
		self.no_reg_or_mem_placeholder = Placeholder(
			"No Register Banks or Memories",
			"The connected device does not have any known register banks "+
			"or memories.",
			gtk.STOCK_DIALOG_WARNING)
	
	
	def _remove_main_widgets(self):
		"""
		Remove all main widgets (and any container they're in).
		"""
		top_level_containers = set()
		
		# Scan each widget removing it from its container and checking if it is not
		# the vbox.
		for widget in (self.register_viewer,
		               self.mem_panes,
		               self.unknown_device_placeholder,
		               self.no_reg_or_mem_placeholder):
			parent = widget.get_parent()
			if parent is not None:
				# Remove the widget from wherever it is contained
				parent.remove(widget)
				
				# Was this widget is contained in some sub-container
				if parent is not self.vbox:
					top_level_containers.add(parent)
			
		
		# Remove the top-level containers
		for container in top_level_containers:
			self.vbox.remove(container)
			container.destroy()
	
	
	def _set_mem_reg_visible(self, enable_memory_viewer, enable_register_viewer):
		"""
		Will show the memory and/or register viewer widgets (or a placeholder) in
		the main window.
		"""
		self._remove_main_widgets()
		
		# The widget to insert into the main space of the window
		top_level_widget = None
		
		if enable_memory_viewer and enable_register_viewer:
			# Both memory and registers are visible, show them in two panes
			top_level_widget = gtk.HPaned()
			top_level_widget.pack1(self.register_viewer, shrink = False)
			top_level_widget.pack2(self.mem_panes, shrink = False)
		elif enable_memory_viewer:
			# Only the memory viewer is enabled
			top_level_widget = self.mem_panes
		elif enable_register_viewer:
			# Only the register viewer is enabled
			top_level_widget = self.register_viewer
		elif self.system.architecture is not None:
			# The architecture doesn't feature either memory or registers
			top_level_widget = self.no_reg_or_mem_placeholder
		else:
			# The architecture is unknown
			top_level_widget = self.unknown_device_placeholder
		
		# Insert the widget into the main slot of the window
		self.vbox.pack_start(top_level_widget, expand = True, fill = True)
		self.vbox.reorder_child(top_level_widget, 1)
		top_level_widget.show_all()
	
	
	def _init_gui(self):
		"""
		Set up the GUI and all its widgets!
		"""
		# Default window size
		self.set_default_size(*MainWindow.DEFAULT_SIZE)
		
		self.vbox = gtk.VBox()
		self.add(self.vbox)
		
		# Add the control/menu bar
		self.vbox.pack_start(self.control_bar, expand = False, fill = True)
		
		# Add a movable division between the register and memory viewers
		reg_mem_panes = gtk.HPaned()
		self.vbox.pack_start(reg_mem_panes, expand = True, fill = True)
		
		# Add the register viewer
		reg_mem_panes.pack1(self.register_viewer, shrink = False)
		
		# Add a movable division between the two memory viewers
		self.mem_panes = gtk.VPaned()
		reg_mem_panes.pack2(self.mem_panes, shrink = False)
		
		# Display the memory viewers
		self.mem_panes.pack1(self.memory_viewer_top, resize = True, shrink = False)
		self.mem_panes.pack2(self.memory_viewer_btm, resize = True, shrink = False)
		
		# Ensure the log viewer is a decent size
		self.log_viewer.set_size_request(-1, 200)
		
		# Add an expander with a log viewer in
		self.log_expander = gtk.Expander("Error Log")
		self.log_expander.add(self.log_viewer)
		self.log_expander.set_use_markup(True)
		self.vbox.pack_start(self.log_expander, expand = False, fill = True)
		
		# Expand the viewer if a flagged entry arrives
		self.log_viewer.connect("update", self._on_log_update)
		
		# Clear the unread counter when shown
		self.log_expander.connect("activate", self._on_log_update, None)
		
		# Add a status-bar
		self.vbox.pack_start(self.status_bar, fill=True, expand=False)
		
		# Tick the auto-refresh box
		self.control_bar.auto_refresh_button.set_active(True)
		
		# Quit when closing this window
		self.connect("destroy", self._on_quit_clicked)
	
	
	def _init_adjustments(self):
		"""
		Add all background process progress adjustments to the status-bar.
		"""
		
		# Memory image loading
		self.status_bar.add_adjustment(
			ControlBar.loader_background_decorator.get_adjustment(self.control_bar),
			"Loading Memory Image"
		)
		
		# Assembler
		self.status_bar.add_adjustment(
			ControlBar.assembler_background_decorator.get_adjustment(self.control_bar),
			"Assembling File"
		)
	
	
	def _make_container_window(self, title, widget, icon = None, size = None):
		"""
		Make a simple window containing a widget.
		"""
		periph_window = gtk.Window()
		periph_window.set_transient_for(self)
		periph_window.add(widget)
		periph_window.set_title(title)
		if icon is not None:
			periph_window.set_icon(icon)
		if size is not None:
			periph_window.set_default_size(*size)
		
		# Share main window's keyboard shortcuts
		periph_window.add_accel_group(self.control_bar.accelerators)
		
		return periph_window
	
	
	def _init_periph(self, periph_num, periph_id, periph_sub_id,
	                 name, PeripheralWidget):
		"""
		Instantiate and create a window for this peripheral viewer and hook up all
		events.
		"""
		periph_widget = PeripheralWidget(self.system, periph_num,
		                                 periph_id, periph_sub_id)
		periph_widget.connect("global-refresh", self._on_device_state_changed)
		
		self.periph_viewers.append(periph_widget)
		
		# Create a window to display the widget
		periph_window = self._make_container_window(periph_widget.get_name(),
		                                            periph_widget,
		                                            periph_widget.get_icon(gtk.ICON_SIZE_MENU))
		
		# Prevent being closed (just hide)
		def on_close(window, event):
			# Don't close the window, just hide it
			window.hide()
			return True
		periph_window.connect("delete-event", on_close)
		
		# Add toolbar button to show the window
		def show_periph_window(button):
			periph_window.show_all()
			periph_window.present()
		self.control_bar.add_periph(periph_widget, show_periph_window)
		
		# Add progress monitors
		for adjustment, name in periph_widget.get_progress_adjustments():
			self.status_bar.add_adjustment(adjustment, name)
	
	
	def _destroy_periph(self, periph_widget):
		"""
		Destroy a periph_widget and disconnect everything.
		"""
		periph_window = periph_widget.get_parent()
		
		# Disconnect all the adjustments
		adjustments = periph_widget.get_progress_adjustments()
		for adjustment in adjustments:
			self.status_bar.remove_adjustment(adjustment)
		
		# Remove from the control bar
		self.control_bar.remove_periph(periph_widget)
		
		# Close the window
		periph_window.destroy()
	
	
	@RunInBackground()
	def _update_peripherals(self):
		"""
		Update the peripheral viewers.
		"""
		# Get the periph list in another thread
		periph_ids = self.system.get_peripheral_ids()
		
		# Return to the GTK thread
		yield
		
		# A list of periph_id/periph_sub_id pairs in the same order as the list of
		# periph_viewers for lookups. Entries which have been updated are replaced
		# with None to flag them as being up-to-date.
		cur_periph_ids = [(p.periph_id, p.periph_sub_id) for p in self.periph_viewers]
		
		for periph_num, (periph_id, periph_sub_id) in enumerate(periph_ids):
			if (periph_id, periph_sub_id) in cur_periph_ids:
				# A viewer already been created for this widget, make sure its
				# peripheral number is up-to-date
				index = cur_periph_ids.index((periph_id, periph_sub_id))
				self.periph_viewers[index].architecture_changed(periph_num)
				
				# Widget has been updated
				cur_periph_ids[index] = None
			else:
				# No viewer exists, create one (if possible)
				name, PeripheralWidget = get_peripheral_view(periph_id, periph_sub_id)
				if PeripheralWidget is not None:
					self._init_periph(periph_num, periph_id, periph_sub_id,
					                  name, PeripheralWidget)
		
		# Remove any old peripherals which still remain
		for index, periph_ids in list(enumerate(cur_periph_ids))[::-1]:
			# If the periph_ids has not been cleared to None, it was not updated and
			# thus the corresponding widget should be destroyed.
			if periph_ids is not None:
				periph_viewer = self.periph_viewers.pop(index)
				self._destroy_periph(periph_viewer)
	
	
	def _on_interval(self):
		"""
		Callback called at a regular interval. Update the screen.
		"""
		# Die if killed
		if self.killed:
			return False
		
		# If auto-refresh is enabled, do it!
		if self.control_bar.auto_refresh_button.get_active():
			self.refresh()
		
		# Reschedule the interval
		return True
	
	
	def _on_log_update(self, widget, flag):
		"""
		Called when a new log item appears. If it is flagged, expand the log viewer.
		"""
		if flag:
			self.log_expander.set_expanded(True)
		
		if self.log_expander.get_expanded():
			# Viewer expanded
			self.unread_log_entries = 0
		else:
			if flag is not None: # Flag is none on activate
				self.unread_log_entries += 1
		
		
		if self.unread_log_entries > 0:
			self.log_expander.set_label("<b>Error Log (%d)</b>"%self.unread_log_entries)
		else:
			self.log_expander.set_label("Error Log")
	
	
	def _on_device_state_changed(self, *args):
		"""
		Emitted whenever a change occurs to the device which may cause changes in
		all widgets and thus should force a global refresh.
		"""
		self.refresh()
	
	
	def _on_select_target_clicked(self, btn):
		"""
		Close this window and re-show the initial target selection window.
		"""
		for viewer in self.memory_viewers + self.register_viewers + self.periph_viewers:
			window = viewer.get_parent()
			window.destroy()
		
		# Stop the refresh timer
		self.killed = True
		
		self.handler_block_by_func(self._on_quit_clicked)
		self.emit("change-target")
		self.destroy()
		
	
	
	def _on_quit_clicked(self, btn):
		"""
		Quit the program, NOW!
		"""
		gtk.main_quit()
	
	
	def _on_new_viewer_clicked(self, btn, ViewerType, viewer_list, title):
		"""
		Create a new viewer in a window.
		
		ViewerType is a class which implements a viewer widget.
		
		viewer_list is a list to which the viewer widget will be added and then
		removed when the window is closed.
		
		title is a title for the window.
		"""
		viewer = ViewerType(self.system)
		
		def on_close(window, event, viewer):
			viewer_list.remove(viewer)
		
		window = self._make_container_window(title, viewer, size = (600,500))
		window.connect("delete-event", on_close, viewer)
		
		window.show_all()
		viewer.refresh()
		
		viewer_list.append(viewer)
	
	
	@RunInBackground()
	def refresh(self):
		"""
		Refresh all widgets' data
		"""
		self.system.clear_cache()
		
		# Check to see if the architecture changed
		board_changed = self.system.get_board_definition_changed()
		
		if board_changed:
			self.system.update_architecture()
		
		# Return to GTK thread
		yield
		
		if board_changed:
			self.architecture_changed()
			return
		
		# Board didn't change, just refresh as-per-usual
		self.control_bar.refresh()
		self.status_bar.refresh()
		
		# Don't update the register windows unless we actually have some
		if self.system.architecture is not None:
			if self.system.architecture.memories:
				self.memory_viewer_top.refresh()
				self.memory_viewer_btm.refresh()
			if self.system.architecture.register_banks:
				self.register_viewer.refresh()
		
		# Update all viewer widgeglobal-refresh
		for viewer in self.memory_viewers + self.register_viewers + self.periph_viewers:
			viewer.refresh()
	
	
	def _architecture_changed(self):
		"""
		Local version for just the MainWindow's own widgets. Does not propogate out.
		Called when the architecture changes, deals with all the
		architecture-specific changes which need to be made to the GUI.
		"""
		# Set the window title
		self.set_title("%s (%s) — %s v%s"%(
			(self.system.architecture.name
			 if self.system.architecture is not None
			 else "Unknown Architecture"),
			self.system.back_end.name,
			about.NAME,
			about.VERSION,
		))
		
		# Update the peripherals available
		self._update_peripherals()
		
		# Display the appropriate main viewers (or display a placeholder)
		enable_memory_viewer   = False
		enable_register_viewer = False
		if self.system.architecture is not None:
			enable_memory_viewer   = len(self.system.architecture.memories) > 0
			enable_register_viewer = len(self.system.architecture.register_banks) > 0
		self._set_mem_reg_visible(enable_memory_viewer, enable_register_viewer)
		
		self.refresh()
	
	def architecture_changed(self):
		"""
		Called when the architecture changes, deals with all the
		architecture-specific changes which need to be made to the GUI.
		"""
		# Update this window's widgets
		self._architecture_changed()
		
		# Propogate to widgets
		self.control_bar.architecture_changed()
		self.register_viewer.architecture_changed()
		self.log_viewer.architecture_changed()
		self.status_bar.architecture_changed()
		self.memory_viewer_top.architecture_changed()
		self.memory_viewer_btm.architecture_changed()
		
		for viewer in self.memory_viewers + self.register_viewers:
			viewer.architecture_changed()