Modules | Files | Inheritance Tree | Inheritance Graph | Name Index | Config
File: Synopsis/UI/Qt/project.py
    1| # $Id: project.py,v 1.6 2002/10/11 06:03:23 chalky Exp $
    2| #
    3| # This file is a part of Synopsis.
    4| # Copyright (C) 2000, 2001 Stefan Seefeld
    5| # Copyright (C) 2000, 2001 Stephen Davies
    6| #
    7| # Synopsis is free software; you can redistribute it and/or modify it
    8| # under the terms of the GNU General Public License as published by
    9| # the Free Software Foundation; either version 2 of the License, or
   10| # (at your option) any later version.
   11| #
   12| # This program is distributed in the hope that it will be useful,
   13| # but WITHOUT ANY WARRANTY; without even the implied warranty of
   14| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   15| # General Public License for more details.
   16| #
   17| # You should have received a copy of the GNU General Public License
   18| # along with this program; if not, write to the Free Software
   19| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   20| # 02111-1307, USA.
   21| #
   22| # $Log: project.py,v $
   23| # Revision 1.6  2002/10/11 06:03:23  chalky
   24| # Use config from project
   25| #
   26| # Revision 1.5  2002/09/28 05:53:31  chalky
   27| # Refactored display into separate project and browser windows. Execute projects
   28| # in the background
   29| #
   30| # Revision 1.4  2002/06/22 07:03:27  chalky
   31| # Updates to GUI - better editing of Actions, and can now execute some.
   32| #
   33| # Revision 1.3  2002/01/09 10:16:35  chalky
   34| # Centralized navigation, clicking links in (html) docs works.
   35| #
   36| # Revision 1.2  2001/11/09 08:06:59  chalky
   37| # More GUI fixes and stuff. Double click on Source Actions for a dialog.
   38| #
   39| # Revision 1.1  2001/11/07 05:58:21  chalky
   40| # Reorganised UI, opening a .syn file now builds a simple project to view it
   41| #
   42| 
   43| """The project window. Displays the output of the project."""
   44| 
   45| import os, sys, tempfile, traceback
   46| from qt import *
   47| from Synopsis.Core import Action, Executor, AST
   48| from Synopsis.Core.Project import Project
   49| 
   50| from actionvis import CanvasWindow
   51| from browse import BrowserWindow
   52| 
   53| class ProjectWindow (QSplitter):
   54|     def __init__(self, main_window, filename):
   55|         QSplitter.__init__(self, main_window.workspace)
   56|         self.setOrientation(Qt.Vertical)
   57|         self.setCaption('Project Window')
   58| 
   59|         self.main_window = main_window
   60|         self.project = Project()
   61|         self.browser = None
   62|         self.__activated = 0
   63| 
   64|         self.actions_display = CanvasWindow(self, self.main_window, self.project)
   65|         self.exec_output = QMultiLineEdit(self)
   66|         self.exec_output.setReadOnly(1)
   67|         self.exec_output.hide()
   68| 
   69|         if filename:
   70|             self.project.load(filename)
   71|             self.setCaption(self.project.name()+' : Project Window')
   72| 
   73|         self.connect(self.parent(), SIGNAL('windowActivated(QWidget*)'), self.windowActivated)
   74|         
   75|         #CanvasWindow(parent, main_window, self.project)
   76|         
   77|         self.setSizes([30,70])
   78|         #self.showMaximized()
   79|         #self.show()
   80|         main_window.add_window(self)
   81| 
   82|     def windowActivated(self, widget):
   83|         if self.__activated:
   84|             if widget is not self: self.deactivate()
   85|         elif widget is self: self.activate()
   86|     
   87|     def activate(self):
   88|         self.__activated = 1
   89|         self._menu_id = self.main_window.menuBar().insertItem('&Project', self.main_window.project_menu)
   90| 
   91|     def deactivate(self):
   92|         self.__activated = 0
   93|         self.main_window.menuBar().removeItem(self._menu_id)
   94| 
   95| 
   96|     def execute_project(self):
   97|         """Tries to show some output in the browser parts of the project
   98|         window. To do this is searches for an appropriate FormatAction (ie,
   99|         one that has HTML options), checks that everything is up to date, and
  100|         loads the AST into the browser windows."""
  101|         # Find output
  102|         formatter = None
  103|         if formatter is None:
  104|             # Try to get default from Project
  105|             formatter = self.project.default_formatter()
  106|         if formatter is None:
  107|             # Try to find a HTML formatter
  108|             for action in self.project.actions().actions():
  109|                if isinstance(action, Action.FormatAction):
  110|                   # Check html..
  111|                    formatter = action
  112|         if not formatter:
  113|             # Maybe tell user..
  114|           return
  115|         self.format_action = formatter
  116| 
  117|         # Find input to formatter
  118|         inputs = formatter.inputs()
  119|         if len(inputs) != 1:
  120|             # Maybe tell user..
  121|           return
  122|         self.cache_action = cache = inputs[0]
  123|         if not isinstance(cache, Action.CacherAction) or len(cache.inputs()) != 1:
  124|             print "Error: Selected formatter must have a cache with a single input"
  125|           return
  126| 
  127|         # Now that we have verified the action to use, fork a process to do
  128|         # the work in the background
  129|         self.tempfile = file = tempfile.TemporaryFile('w+b', 64, 'synopsis-output')
  130|         self.temp_pos = 0
  131| 
  132|         #TODO: use popen for windows compat.
  133|         self.child_pid = os.fork()
  134|         if self.child_pid == 0:
  135|         try:
  136|                # Redirect output to file
  137|                os.dup2(self.tempfile.fileno(), 1)
  138|                os.dup2(self.tempfile.fileno(), 2)
  139|                print "Executing project "+self.project.name()
  140|                # Create executor for the input
  141|                input_exec = Executor.ExecutorCreator(self.project, 1).create(cache)
  142| 
  143|                # Get AST from the input (forces project creation)
  144|                names = input_exec.get_output_names()
  145|                ast = input_exec.get_output(names[0][0])
  146|                # Ignore the ast, since parent process will read it
  147|                print "Done."
  148|            except:
  149|                print "An exception occurred"
  150|                traceback.print_exc()
  151|             self.tempfile.flush()
  152|             sys.exit(0)
  153|         
  154|         # Create a timer
  155|         self.timer = QTimer()
  156|         self.timer.changeInterval(100)
  157|         self.connect(self.timer, SIGNAL('timeout()'), self.checkThreadOutput)
  158| 
  159|         self.exec_output.show()
  160|         self.setSizes([80,20])
  161| 
  162|     def checkThreadOutput(self):
  163|         """This method is called periodically to check output on the
  164|         tempfile"""
  165| 
  166|         # Read doesn't work, so we have to pull these seek tricks
  167|         self.tempfile.seek(0, 2)
  168|         end = self.tempfile.tell()
  169|         if end != self.temp_pos:
  170|             self.tempfile.seek(self.temp_pos)
  171|             input = self.tempfile.read(end - self.temp_pos)
  172|             self.temp_pos = end
  173|             self.exec_output.append(input)
  174|             self.exec_output.setCursorPosition(self.exec_output.numLines(), 0)
  175|         
  176|         # Check if child finished
  177|         pid, status = os.waitpid(self.child_pid, os.WNOHANG)
  178|         if pid != 0:
  179|             # Thread ended
  180|             # Give AST to browser
  181|             if not self.browser:
  182|                config = self.format_action.config()
  183|                self.browser = BrowserWindow(self.main_window, None, self, config)
  184|                self.browser.setCaption(self.project.name()+' : Browser Window')
  185| 
  186|             # Now load ast from cache
  187|         try:
  188|                input_exec = Executor.ExecutorCreator(self.project, 0).create(self.cache_action)
  189|                file = input_exec.get_cache_filename(input_exec.get_output_names()[0][0])
  190|                ast = AST.load(file)
  191|         if ast:
  192|                    self.browser.set_current_ast(ast)
  193|            except:
  194|                print "An exception occurred"
  195|                traceback.print_exc()
  196| 
  197|             # Destroy timer
  198|             self.timer.stop()
  199|             del self.timer
  200| 
  201|             # Restore file descriptors
  202|             self.tempfile.close()
  203|             del self.tempfile
  204|         
  205| 
  206|