home *** CD-ROM | disk | FTP | other *** search
/ tusportal.tus.k12.pa.us / tusportal.tus.k12.pa.us.tar / tusportal.tus.k12.pa.us / Wyse / latest-image.raw / 0.img / usr / lib / pygtk / 2.0 / demos / changedisplay.py < prev    next >
Text File  |  2010-05-11  |  14KB  |  414 lines

  1. #!/usr/bin/env python
  2. '''Change Display
  3.  
  4. Demonstrates migrating a window between different displays and
  5. screens. A display is a mouse and keyboard with some number of
  6. associated monitors. A screen is a set of monitors grouped
  7. into a single physical work area. The neat thing about having
  8. multiple displays is that they can be on a completely separate
  9. computers, as long as there is a network connection to the
  10. computer where the application is running.
  11.  
  12. Only some of the windowing systems where GTK+ runs have the
  13. concept of multiple displays and screens. (The X Window System
  14. is the main example.) Other windowing systems can only
  15. handle one keyboard and mouse, and combine all monitors into
  16. a single screen.
  17.  
  18. This is a moderately complex example, and demonstrates:
  19.  
  20. - Tracking the currently open displays and screens
  21. - Changing the screen for a window
  22. - Letting the user choose a window by clicking on it
  23. - Using GtkListStore and GtkTreeView
  24. - Using GtkDialog
  25. '''
  26. import gtk
  27. import gobject
  28.  
  29. # These enumerations provide symbolic names for the columns
  30. # in the two GtkListStore models.
  31. #
  32. (
  33.   DISPLAY_COLUMN_NAME,
  34.   DISPLAY_COLUMN_DISPLAY,
  35.   DISPLAY_NUM_COLUMNS
  36. ) = range(3)
  37.  
  38. (
  39.   SCREEN_COLUMN_NUMBER,
  40.   SCREEN_COLUMN_SCREEN,
  41.   SCREEN_NUM_COLUMNS
  42. ) = range(3)
  43.  
  44. def find_toplevel_at_pointer(display):
  45.     ''' Finds the toplevel window under the mouse pointer, if any.
  46.     '''
  47.     pointer_window = display.get_window_at_pointer()[0]
  48.  
  49.     # The user data field of a GdkWindow is used to store a pointer
  50.     # to the widget that created it.
  51.     #
  52.     if pointer_window:
  53.         widget = pointer_window.get_user_data()
  54.  
  55.     return widget and widget.get_toplevel() or None
  56.  
  57. class QueryForToplevel(gtk.Window):
  58.     ''' Asks the user to click on a window, then waits for them click
  59.         the mouse. When the mouse is released, returns the toplevel
  60.         window under the pointer, or NULL, if there is none.
  61.     '''
  62.  
  63.     def __init__(self, screen, prompt):
  64.         gtk.Window.__init__(self, gtk.WINDOW_POPUP)
  65.         self.set_screen(screen)
  66.         self.set_modal(True)
  67.         self.set_position(gtk.WIN_POS_CENTER)
  68.  
  69.         frame = gtk.Frame()
  70.         frame.set_shadow_type(gtk.SHADOW_OUT)
  71.         self.add(frame)
  72.  
  73.         label = gtk.Label(prompt)
  74.         label.set_padding(10, 10)
  75.         frame.add(label)
  76.  
  77.         self.show_all()
  78.  
  79.     def run(self):
  80.         display = self.get_screen().get_display()
  81.         cursor = gtk.gdk.Cursor(display, gtk.gdk.CROSSHAIR)
  82.  
  83.         main_context = gobject.main_context_default()
  84.         if (gtk.gdk.pointer_grab(self.window, False,
  85.             gtk.gdk.BUTTON_RELEASE_MASK, None, cursor) == gtk.gdk.GRAB_SUCCESS):
  86.             self.query_clicked = False
  87.             self.connect("button-release-event", self.button_release_event_cb)
  88.  
  89.             # Process events until clicked is set by button_release_event_cb.
  90.             # We pass in may_block=True since we want to wait if there
  91.             # are no events currently.
  92.             #
  93.             while self.query_clicked is False:
  94.                 main_context.iteration(True)
  95.  
  96.             toplevel = find_toplevel_at_pointer(display)
  97.             if (toplevel == self):
  98.                 toplevel = None;
  99.  
  100.         self.destroy()
  101.         gtk.gdk.flush()     # Really release the grab
  102.  
  103.         return toplevel
  104.  
  105.     def button_release_event_cb(self, winref, event):
  106.         self.query_clicked = True
  107.         return True
  108.  
  109.  
  110. class LeftAlignButton(gtk.Button):
  111.     ''' If we have a stack of buttons, it often looks better if their contents
  112.         are left-aligned, rather than centered. This class creates a button
  113.         and left-aligns it contents.
  114.     '''
  115.     def __init__(self, label):
  116.         gtk.Button.__init__(self, label)
  117.         child = self.get_children()[0]
  118.         child.set_alignment(0., 0.5)
  119.  
  120.  
  121. # Main entry point. If the dialog for this demo doesn't yet exist, creates
  122. # it.
  123. #
  124. class ChangeDisplayDemo(gtk.Dialog):
  125.     size_group = None
  126.     display_model = None
  127.     screen_model = None
  128.     screen_selection = None
  129.     current_display = None
  130.     current_screen = None
  131.  
  132.     def __init__(self, parent=None):
  133.         gtk.Dialog.__init__(self, "Change Screen or display", parent,
  134.             gtk.DIALOG_NO_SEPARATOR,
  135.             (gtk.STOCK_CLOSE,  gtk.RESPONSE_CLOSE,
  136.              "Change",         gtk.RESPONSE_OK))
  137.         self.set_default_size(300, 400)
  138.  
  139.         try:
  140.             self.set_screen(parent.get_screen())
  141.         except AttributeError:
  142.             self.connect('destroy', lambda *w: gtk.main_quit())
  143.         self.connect("response", self.response_cb)
  144.         self.connect("destroy", self.destroy_cb)
  145.  
  146.         vbox = gtk.VBox(False, 5)
  147.         vbox.set_border_width(8)
  148.  
  149.         self.vbox.pack_start(vbox, True, True, 0)
  150.  
  151.         frame = self.__create_display_frame()
  152.         vbox.pack_start(frame, True, True, 0)
  153.  
  154.         frame = self.__create_screen_frame()
  155.         vbox.pack_start(frame, True, True, 0)
  156.  
  157.         self.__initialize_displays()
  158.  
  159.         self.show_all()
  160.  
  161.     def __initialize_displays(self):
  162.         ''' Adds all currently open displays to our list of displays,
  163.             and set up a signal connection so that we'll be notified
  164.             when displays are opened in the future as well.
  165.         '''
  166.         manager = gtk.gdk.display_manager_get()
  167.         displays = manager.list_displays()
  168.  
  169.         for item in displays:
  170.             self.add_display(item)
  171.         id = manager.connect("display_opened", self.display_opened_cb)
  172.         manager.set_data('user-callback', id)
  173.  
  174.     def __create_frame(self, title):
  175.         ''' This function is used both for creating the "Display" and
  176.             "Screen" frames, since they have a similar structure. The
  177.             caller hooks up the right context for the value returned
  178.             in tree_view, and packs any relevant buttons into button_vbox.
  179.         '''
  180.         frame = gtk.Frame(title)
  181.  
  182.         hbox = gtk.HBox(False, 8)
  183.         hbox.set_border_width(8)
  184.         frame.add(hbox)
  185.  
  186.         scrollwin = gtk.ScrolledWindow();
  187.         scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
  188.         scrollwin.set_shadow_type (gtk.SHADOW_IN)
  189.         hbox.pack_start(scrollwin, True, True, 0)
  190.  
  191.         tree_view = gtk.TreeView()
  192.         tree_view.set_headers_visible(False)
  193.         scrollwin.add(tree_view)
  194.  
  195.         selection = tree_view.get_selection()
  196.         selection.set_mode(gtk.SELECTION_BROWSE)
  197.  
  198.         button_vbox = gtk.VBox(False, 5)
  199.         hbox.pack_start(button_vbox, False, False, 0)
  200.  
  201.         if self.size_group is None:
  202.             self.size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
  203.  
  204.         self.size_group.add_widget(button_vbox)
  205.  
  206.         return (frame, tree_view, button_vbox)
  207.  
  208.  
  209.     def __create_display_frame(self):
  210.         ''' Creates the "Display" frame in the main window.
  211.         '''
  212.         frame, tree_view, button_vbox = self.__create_frame("Display")
  213.  
  214.         button = LeftAlignButton("_Open...")
  215.         button.connect("clicked", self.open_display_cb)
  216.         button_vbox.pack_start(button, False, False, 0)
  217.  
  218.         button = LeftAlignButton("_Close")
  219.         button.connect ("clicked", self.close_display_cb)
  220.         button_vbox.pack_start(button, False, False, 0)
  221.  
  222.         self.display_model = gtk.ListStore(str, object);
  223.         tree_view.set_model(self.display_model)
  224.  
  225.         column = gtk.TreeViewColumn("Name", gtk.CellRendererText(),
  226.             text=DISPLAY_COLUMN_NAME)
  227.         tree_view.append_column(column)
  228.  
  229.         selection = tree_view.get_selection()
  230.         selection.connect("changed", self.display_changed_cb)
  231.  
  232.         return frame
  233.  
  234.     def __create_screen_frame(self):
  235.         ''' Creates the "Screen" frame in the main window.
  236.         '''
  237.         frame, tree_view, button_vbox = self.__create_frame("Screen")
  238.  
  239.         self.screen_model = gtk.ListStore(int, object);
  240.         tree_view.set_model(self.screen_model)
  241.  
  242.         column = gtk.TreeViewColumn("Number", gtk.CellRendererText(),
  243.             text=SCREEN_COLUMN_NUMBER)
  244.         tree_view.append_column(column)
  245.  
  246.         self.screen_selection = tree_view.get_selection()
  247.         self.screen_selection.connect("changed", self.screen_changed_cb)
  248.  
  249.         return frame
  250.  
  251.     def query_change_display(self):
  252.         ''' Prompts the user for a toplevel window to move, and then moves
  253.             that window to the currently selected display
  254.         '''
  255.         screen = self.window.get_screen()
  256.  
  257.         toplevel = QueryForToplevel(screen,
  258.             "Please select the toplevel\nto move to the new screen").run()
  259.  
  260.         if toplevel is not None:
  261.             toplevel.set_screen(self.current_screen)
  262.         else:
  263.             screen.get_display().beep()
  264.  
  265.  
  266.     def response_cb(self, dialog, response_id):
  267.         ''' Called when the user clicks on a button in our dialog or
  268.             closes the dialog through the window manager. Unless the
  269.             "Change" button was clicked, we destroy the dialog.
  270.         '''
  271.         if response_id == gtk.RESPONSE_OK:
  272.             self.query_change_display()
  273.         else:
  274.             dialog.destroy()
  275.  
  276.     def open_display_cb(self, button):
  277.         ''' Called when the user clicks on "Open..." in the display
  278.             frame. Prompts for a new display, and then opens a connection
  279.             to that display.
  280.         '''
  281.         dialog = gtk.Dialog("Open Display", self, gtk.DIALOG_MODAL,
  282.             (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
  283.  
  284.         dialog.set_default_response(gtk.RESPONSE_OK)
  285.         display_entry = gtk.Entry()
  286.         display_entry.set_activates_default(True)
  287.         dialog_label = gtk.Label("Please enter the name of\nthe new display\n")
  288.  
  289.         dialog.vbox.add(dialog_label)
  290.         dialog.vbox.add(display_entry)
  291.  
  292.         display_entry.grab_focus()
  293.         dialog.show_all()
  294.  
  295.         result = None
  296.         while result is None:
  297.             response_id = dialog.run()
  298.             if response_id != gtk.RESPONSE_OK:
  299.                 break;
  300.             new_screen_name = display_entry.get_chars(0, -1)
  301.             print new_screen_name
  302.             if new_screen_name != "":
  303.                 result = gtk.gdk.Display(new_screen_name)
  304.                 if result is None:
  305.                     error_msg = (
  306.                     "Can't open display :\n\t%s\nplease try another one\n" %
  307.                     (new_screen_name,))
  308.                     dialog_label.set_text(error_msg)
  309.  
  310.         dialog.destroy()
  311.  
  312.     def close_display_cb(self, button):
  313.         ''' Called when the user clicks on the "Close" button in the
  314.             "Display" frame. Closes the selected display.
  315.         '''
  316.         if self.current_display:
  317.             self.current_display.close()
  318.  
  319.  
  320.     def display_changed_cb(self, selection):
  321.         ''' Called when the selected row in the display list changes.
  322.             Updates info.current_display, then refills the list of
  323.             screens.
  324.         '''
  325.         model, iter = selection.get_selected()
  326.         if iter is not None:
  327.             self.current_display = model.get_value(iter, DISPLAY_COLUMN_DISPLAY)
  328.         else:
  329.             self.current_display = None
  330.         self.fill_screens()
  331.  
  332.     def screen_changed_cb(self, selection):
  333.         ''' Called when the selected row in the sceen list changes.
  334.             Updates info->current_screen.
  335.         '''
  336.         model, iter = selection.get_selected()
  337.         if iter:
  338.             self.current_screen = model.get(iter, SCREEN_COLUMN_SCREEN)[0]
  339.         else:
  340.             self.current_screen = None;
  341.  
  342.     def destroy_cb(self, parent):
  343.         self.destroy_info()
  344.         if parent is None:
  345.             gtk.main_quit()
  346.  
  347.  
  348.     def fill_screens(self):
  349.         ''' Fills in the screen list based on the current display
  350.         '''
  351.         self.screen_model.clear()
  352.         if self.current_display is not None:
  353.             n_screens = self.current_display.get_n_screens()
  354.  
  355.             for i in range(n_screens):
  356.                 screen = self.current_display.get_screen(i);
  357.                 iter = self.screen_model.append()
  358.                 self.screen_model.set(iter,
  359.                     SCREEN_COLUMN_NUMBER, i, SCREEN_COLUMN_SCREEN, screen)
  360.                 if (i == 0):
  361.                     self.screen_selection.select_iter(iter)
  362.  
  363.     def display_closed_cb(self, display, is_error, info):
  364.         ''' Called when one of the currently open displays is closed.
  365.             Remove it from our list of displays.
  366.         '''
  367.         iter = self.display_model.get_iter_first()
  368.         while iter:
  369.             tmp_display = self.display_model.get_value(iter, DISPLAY_COLUMN_DISPLAY)
  370.             if (tmp_display == display):
  371.                 info.display_model.remove(iter)
  372.                 break;
  373.             iter = info.display_model.iter_next()
  374.  
  375.     def add_display(self, display):
  376.         ''' Adds a new display to our list of displays, and connects
  377.             to the "closed" signal so that we can remove it from the
  378.             list of displays again.
  379.         '''
  380.         name = display.get_name()
  381.  
  382.         iter = self.display_model.append()
  383.         self.display_model.set(iter,
  384.             DISPLAY_COLUMN_NAME, name, DISPLAY_COLUMN_DISPLAY, display)
  385.         id = display.connect("closed", self.display_closed_cb)
  386.         display.set_data('user-callback', id)
  387.  
  388.     def display_opened_cb(self, manager, display):
  389.         ''' Called when a new display is opened
  390.         '''
  391.         self.add_display(display)
  392.  
  393.     def destroy_info(self):
  394.         ''' Cleans up when the toplevel is destroyed; we remove the
  395.             connections we use to track currently open displays.
  396.         '''
  397.         manager = gtk.gdk.display_manager_get()
  398.         displays = manager.list_displays()
  399.  
  400.         id = manager.get_data('user-callback')
  401.         manager.disconnect(id)
  402.  
  403.         for tmp_list in displays:
  404.             id = tmp_list.get_data('user-callback')
  405.             tmp_list.disconnect(id)
  406.  
  407.  
  408. def main():
  409.     ChangeDisplayDemo()
  410.     gtk.main()
  411.  
  412. if __name__ == '__main__':
  413.     main()
  414.