Exemplo n.º 1
0
 def attach(self):
   # make a view for the document
   self.document_view = DocumentView(parent=self,
     document=self.document)
   # add it to the document
   self.stack.addWidget(self.document_view)
   # update actions when relevant objects change
   self.document.transport.add_observer(self.update_actions)
   self.document.view_scale.add_observer(self.update_actions)
   self.update_actions()
Exemplo n.º 2
0
class DocumentWindow(QMainWindow):
  def __init__(self, app=None):
    QMainWindow.__init__(self)
    self._app = app
    self.setMinimumSize(QSize(600, 400))
    self.setWindowTitle('New Document')
    # start with no document
    self._document = None
    self.document_view = None
    # make a stack to hold the document
    self.stack = QStackedWidget(self)
    self.setCentralWidget(self.stack)
    # build the menu and toolbar
    self._make_menus()
    # set up stylesheets for look and feel
    self._init_style()
    
  @property
  def document(self):
    return(self._document)
  @document.setter
  def document(self, value):
    # detach from the old document
    if (self._document is not None):
      self.detach()
      self._document = None
    # attach to the new document
    if (value is not None):
      self._document = value
      self.attach()
      
  def detach(self):
    self.document.transport.remove_observer(self.update_actions)
    self.document.view_scale.remove_observer(self.update_actions)
    # remove the document view
    if (self.document_view is not None):
      view = self.document_view
      self.document_view = None
      view.destroy()
      self.stack.removeWidget(view)
    # dump the undo stack and clear the selection
    UndoManager.reset()
  def attach(self):
    # make a view for the document
    self.document_view = DocumentView(parent=self,
      document=self.document)
    # add it to the document
    self.stack.addWidget(self.document_view)
    # update actions when relevant objects change
    self.document.transport.add_observer(self.update_actions)
    self.document.view_scale.add_observer(self.update_actions)
    self.update_actions()
  
  # build the application menu and toolbar
  def _make_menus(self):
    menubar = self.menuBar()
    
    # file menu
    file_menu = menubar.addMenu('&File')
    # new
    new_action = QAction(QIcon.fromTheme('document-new'), '&New', self)
    new_action.setShortcut('Ctrl+O')
    new_action.setStatusTip('Open a document')
    new_action.triggered.connect(self.file_new)
    file_menu.addAction(new_action)
    # open
    open_action = QAction(QIcon.fromTheme('document-open'), '&Open', self)
    open_action.setShortcut('Ctrl+O')
    open_action.setStatusTip('Open a document')
    open_action.triggered.connect(self.file_open)
    file_menu.addAction(open_action)
    # save
    save_action = QAction(QIcon.fromTheme('document-save'), '&Save', self)
    save_action.setShortcut('Ctrl+S')
    save_action.setStatusTip('Save the document')
    save_action.triggered.connect(self.file_save)
    file_menu.addAction(save_action)
    # save as
    save_as_action = QAction(QIcon.fromTheme('document-save'), 'Save &As', self)
    save_as_action.setStatusTip('Save the document to a different file')
    save_as_action.triggered.connect(self.file_save_as)
    file_menu.addAction(save_as_action)
    # ---
    file_menu.addSeparator()
    # quit
    quit_action = QAction(QIcon.fromTheme('application-exit'), '&Quit', self)
    quit_action.setShortcut('Ctrl+Q')
    quit_action.setStatusTip('Quit application')
    quit_action.triggered.connect(self.close)
    file_menu.addAction(quit_action)
    
    # edit menu
    edit_menu = menubar.addMenu('&Edit')
    # undo
    self.undo_action = QAction(icon.get('undo'), '&Undo', self)
    self.undo_action.setShortcut('Ctrl+Z')
    self.undo_action.setStatusTip('Undo the last action')
    self.undo_action.triggered.connect(self.edit_undo)
    edit_menu.addAction(self.undo_action)
    # redo
    self.redo_action = QAction(icon.get('redo'), '&Redo', self)
    self.redo_action.setShortcut('Ctrl+Shift+Z')
    self.redo_action.setStatusTip('Redo the last action that was undone')
    self.redo_action.triggered.connect(self.edit_redo)
    edit_menu.addAction(self.redo_action)
    
    # transport menu
    transport_menu = menubar.addMenu('&Transport')
    # go to start
    self.beginning_action = QAction(icon.get('beginning'), 'Jump to &Beginning', self)
    self.beginning_action.setShortcut('Home')
    self.beginning_action.setStatusTip('Jump back to the beginning of the project')
    self.beginning_action.triggered.connect(self.transport_beginning)
    transport_menu.addAction(self.beginning_action)
    # go to end
    self.end_action = QAction(icon.get('ending'), 'Jump to &End', self)
    self.end_action.setShortcut('End')
    self.end_action.setStatusTip('Jump forward to the end of the project')
    self.end_action.triggered.connect(self.transport_end)
    transport_menu.addAction(self.end_action)
    # back
    self.back_action = QAction(icon.get('backward'), 'Bac&k', self)
    self.back_action.setShortcut('PgUp')
    self.back_action.setStatusTip('Skip backward in time')
    self.back_action.triggered.connect(self.transport_back)
    transport_menu.addAction(self.back_action)
    # forward
    self.forward_action = QAction(icon.get('forward'), '&Forward', self)
    self.forward_action.setShortcut('PgDown')
    self.forward_action.setStatusTip('Skip forward in time')
    self.forward_action.triggered.connect(self.transport_forward)
    transport_menu.addAction(self.forward_action)
    # ---
    transport_menu.addSeparator()
    # previous mark
    self.previous_mark_action = QAction(icon.get('mark_previous'), 'Previous Mark', self)
    self.previous_mark_action.setShortcut('Ctrl+PgUp')
    self.previous_mark_action.setStatusTip('Skip to the previous marked time')
    self.previous_mark_action.triggered.connect(self.transport_previous_mark)
    transport_menu.addAction(self.previous_mark_action)
    # toggle mark
    self.toggle_mark_action = QAction(icon.get('mark_toggle'), 'Toggle Mark', self)
    self.toggle_mark_action.setShortcut('Ctrl+\\')
    self.toggle_mark_action.setStatusTip('Toggle a mark at the current time')
    self.toggle_mark_action.triggered.connect(self.transport_toggle_mark)
    transport_menu.addAction(self.toggle_mark_action)
    # next mark
    self.next_mark_action = QAction(icon.get('mark_next'), 'Next Mark', self)
    self.next_mark_action.setShortcut('Ctrl+PgDown')
    self.next_mark_action.setStatusTip('Skip to the next marked time')
    self.next_mark_action.triggered.connect(self.transport_next_mark)
    transport_menu.addAction(self.next_mark_action)
    # cycle
    self.toggle_cycle_action = QAction(icon.get('cycle'), 'Cycle', self)
    self.toggle_cycle_action.setShortcut('Ctrl+L')
    self.toggle_cycle_action.setStatusTip('Toggle cycling playback mode')
    self.toggle_cycle_action.setCheckable(True)
    self.toggle_cycle_action.toggled.connect(self.transport_toggle_cycle)
    transport_menu.addAction(self.toggle_cycle_action)
    # ---
    transport_menu.addSeparator()
    # stop
    self.stop_action = QAction(icon.get('stop'), '&Stop', self)
    self.stop_action.setStatusTip('Stop playback or recording')
    self.stop_action.triggered.connect(self.transport_stop)
    transport_menu.addAction(self.stop_action)
    # play
    self.play_action = QAction(icon.get('play'), '&Play', self)
    self.play_action.setStatusTip('Start playback')
    self.play_action.triggered.connect(self.transport_play)
    transport_menu.addAction(self.play_action)
    # record
    self.record_action = QAction(icon.get('record'), '&Record', self)
    self.record_action.setStatusTip('Start recording')
    self.record_action.triggered.connect(self.transport_record)
    transport_menu.addAction(self.record_action)
    # ---
    transport_menu.addSeparator()
    # zoom in
    self.zoom_in_action = QAction(icon.get('zoom_in'), 'Zoom &In', self)
    self.zoom_in_action.setShortcut('Ctrl+Shift+Plus')
    self.zoom_in_action.setStatusTip('Zoom in')
    self.zoom_in_action.triggered.connect(self.transport_zoom_in)
    transport_menu.addAction(self.zoom_in_action)
    # zoom out
    self.zoom_out_action = QAction(icon.get('zoom_out'), 'Zoom &Out', self)
    self.zoom_out_action.setShortcut('Ctrl+Shift+Minus')
    self.zoom_out_action.setStatusTip('Zoom out')
    self.zoom_out_action.triggered.connect(self.transport_zoom_out)
    transport_menu.addAction(self.zoom_out_action)
    
    # toolbar
    self.toolbar = self.addToolBar('Main')
    self.toolbar.addAction(self.undo_action)
    self.toolbar.addAction(self.redo_action)
    self.toolbar.addSeparator()
    self.toolbar.addAction(self.beginning_action)
    self.toolbar.addAction(self.back_action)
    self.toolbar.addAction(self.forward_action)
    self.toolbar.addAction(self.end_action)
    self.toolbar.addSeparator()
    self.toolbar.addAction(self.stop_action)
    self.toolbar.addAction(self.play_action)
    self.toolbar.addAction(self.record_action)
    self.toolbar.addSeparator()
    self.toolbar.addAction(self.zoom_out_action)
    self.toolbar.addAction(self.zoom_in_action)
    # give actions their initial states
    UndoManager.add_observer(self.update_actions)
    self.update_actions()
    
  # start a new document
  def file_new(self):
    self.document = doc.Document()
  # open an existing document
  def file_open(self):
    (path, group) = QFileDialog.getOpenFileName(self,
      "Open Project", "~", "Project Files (*.jdp *.yml);;All Files (*.*)")
    document = doc.Document.get_from_path(path)
    if (document is not None):
      self.document = document
  # save the document
  def file_save(self):
    if (not self.document): return
    if (not self.document.path):
      self.file_save_as()
    else:
      self.document.save()
  # save the document with a different file name
  def file_save_as(self):
    (path, group) = QFileDialog.getSaveFileName(self,
        "Save Project", "~", "Project Files (*.jdp *.yml);;All Files (*.*)")
    if (len(path) == 0): return
    self.document.path = path
    self.document.save()
  
  # undo
  def edit_undo(self):
    UndoManager.undo()
  # redo
  def edit_redo(self):
    UndoManager.redo()
  
  # transport actions
  def transport_beginning(self):
    if (self.document):
      self.document.transport.go_to_beginning()
  def transport_end(self):
    if (self.document):
      self.document.transport.go_to_end()
  def transport_back(self):
    if (self.document):
      self.document.transport.skip_back()
  def transport_forward(self):
    if (self.document):
      self.document.transport.skip_forward()
  def transport_previous_mark(self):
    if (self.document):
      self.document.transport.previous_mark()
  def transport_toggle_mark(self):
    if (self.document):
      self.document.transport.toggle_mark()
  def transport_next_mark(self):
    if (self.document):
      self.document.transport.next_mark()
  def transport_toggle_cycle(self, toggled):
    if (self.document):
      self.document.transport.cycling = toggled
  def transport_play(self):
    if (self.document):
      self.document.transport.play()
  def transport_record(self):
    if (self.document):
      self.document.transport.record()
  def transport_stop(self):
    if (self.document):
      self.document.transport.stop()
  def transport_zoom_in(self):
    if (self.document_view):
      self.document_view.zoom_in()
  def transport_zoom_out(self):
    if (self.document):
      self.document_view.zoom_out()
      
  # reflect changes to models in the action buttons
  def update_actions(self):
    # disable undo/redo at the ends of the stack
    self.undo_action.setEnabled(UndoManager.can_undo)
    self.redo_action.setEnabled(UndoManager.can_redo)
    # only allow transport actions if we have a document
    self.beginning_action.setEnabled(self.document is not None)
    self.end_action.setEnabled(self.document is not None)
    self.back_action.setEnabled(self.document is not None)
    self.forward_action.setEnabled(self.document is not None)
    self.stop_action.setEnabled(self.document is not None)
    self.play_action.setEnabled(self.document is not None)
    self.record_action.setEnabled(self.document is not None)
    # check and uncheck the cycling item when cycling changes
    self.toggle_cycle_action.setChecked((self.document is not None) and 
                                        (self.document.transport.cycling))
    # disable zoom actions at the outer limits
    self.zoom_in_action.setEnabled(
      (self.document_view is not None) and (self.document_view.can_zoom_in))
    self.zoom_out_action.setEnabled(
      (self.document_view is not None) and (self.document_view.can_zoom_out))

  # set up stylesheets for the app
  def _init_style(self):
    # style scrollbars to be simple lozenges without buttons
    self._app.setStyleSheet('''
      QScrollBar:horizontal { height: 12px; }
      QScrollBar::handle:horizontal { min-width: 24px; }
      QScrollBar:vertical { width: 12px; }
      QScrollBar::handle:vertical { min-height: 24px; }
      QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
        width: 0;
      }
      QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
        height: 0;
      }
      QScrollBar {
        background: transparent;
        padding: 2px;
      }
      QScrollBar::handle {
        background: rgba(0, 0, 0, 64);
        border-radius: 4px;
      }
      QScrollBar::handle:hover {
        background: rgba(0, 0, 0, 128);
      }
      QScrollBar::add-page, QScrollBar::sub-page {
        background: transparent;
      }
    ''')