Changeset 280


Ignore:
Timestamp:
09/11/09 03:22:41 (10 years ago)
Author:
bretzel_parpg
Message:

Patch by Bretzel.

  • Wrote a parser for the new syntax I developed (I will post about this at the forums in the Dialog System Implementation thread)
  • Started to add a dialogue map, which will show you the flow of the dialogue
  • Overrode the resize event in the application so that it will also resize the editor and dialog map
Location:
trunk/tools/writing_editor/scripts
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/tools/writing_editor/scripts/dialogMap.py

    r251 r280  
    1717 
    1818from PyQt4 import QtGui, QtCore 
    19 from scripts.parser import Parser 
     19from scripts.parser import parse 
    2020 
     21class DialogScene(object): 
     22    """ 
     23    The scene that contains the dialog map 
     24    """ 
     25    def __init__(self, widget): 
     26        """ 
     27        Initialize the scene 
     28        @type widget: QtGui.QTextEdit 
     29        @param widget: the widget that contians the text to add objects from 
     30        @return: None 
     31        """ 
     32        self.widget = widget 
     33        self.refreshMap() 
    2134 
    22 class DialogMap(QtGui.QTreeWidget): 
     35    def refreshMap(self): 
     36        """ 
     37        Refresh the map to reflect all the new data 
     38        @return: None 
     39        """ 
     40        self.scene = QtGui.QGraphicsScene() 
     41        objects = self.findObjects(self.widget.toPlainText()) 
     42        self.addObjectsFromList(objects) 
     43 
     44    def findObjects(self, text): 
     45        """ 
     46        Parse text and then return the objects 
     47        @return: a list of all the objects 
     48        """ 
     49        objects = [] 
     50 
     51        ptext = parse(text) 
     52        objects.append(ptext) 
     53        for obj in ptext.options: 
     54            objects.append(obj) 
     55 
     56        return objects 
     57 
     58    def addObjectsFromList(self, obj_list): 
     59        """ 
     60        Add all of the objects in a list to the dialog map 
     61        @type obj_list: list 
     62        @param obj_list: the list of objects 
     63        @return: None 
     64        """ 
     65        pos = 50 
     66        for obj in obj_list: 
     67 #           print obj 
     68            item = QtGui.QGraphicsEllipseItem(pos,pos,50,50) 
     69            self.scene.addItem(item) 
     70            pos += 10 
     71#        print "Scene list: " + str(self.scene.items()) 
     72     
     73 
     74class DialogMap(QtGui.QGraphicsView): 
    2375    """ 
    24     The dialog map which will show the flow of the dialog 
     76    The dialog map final widget 
    2577    """ 
    2678    def __init__(self, settings, main_edit, parent): 
    2779        """ 
    28         Initialize the dialog map 
    2980        @type settings: settings.Settings 
    3081        @param settings: the settings for the editor 
     
    3687        """ 
    3788        QtGui.QWidget.__init__(self, parent) 
    38          
    39         self.settings = settings 
    40         self.parent = parent 
    41         self.resize(int(self.settings.res_width), int(self.settings.res_height)) 
    42         self.parser = Parser(main_edit, self.handleResult) 
    43  
    44         self.parentItem = None 
    45         self.inOption = False 
    46  
    47         self.setColumnCount(1) 
    48         self.setEditTriggers(self.NoEditTriggers) 
    49         self.setHeaderLabel("") 
    50          
    51     def handleResult(self, result, type_): 
    52         """ 
    53         Take the result, determine where to put it in the dialog map, then put it there 
    54         @type result: string 
    55         @param result: the result to be handled 
    56         @type type_: string 
    57         @param type_: the type of result 
    58         @return: None 
    59         """ 
    60         text = result.split(' ') 
    61  
    62         if (type_ == "SCRIPTNAME"): 
    63             self.setHeaderLabel(text[1]) 
    64  
    65         elif (type_ == "SAY"): 
    66             sayText = text[0] + " says " + text[2] 
    67             self.sayItem = QtGui.QTreeWidgetItem() 
    68             self.sayItem.setText(0, sayText) 
    69             self.insertItem(self.sayItem) 
    70  
    71         elif (type_ == "ATTACK"): 
    72             attackText = text[0] + " attacks " + text[2] 
    73             self.attackItem = QtGui.QTreeWidgetItem() 
    74             self.attackItem.setText(0, attackText) 
    75             self.insertItem(self.attackItem) 
    76  
    77         elif (type_ == "OPTION"): 
    78             optionText = "Option " + text[1] 
    79             self.optionItem = QtGui.QTreeWidgetItem() 
    80             self.optionItem.setText(0, optionText) 
    81             self.insertItem(self.optionItem) 
    82             self.parentItem = self.optionItem 
    83             self.inOption = True 
    84  
    85         elif (self.inOption and type_ == "."): 
    86             option_text = result.split('.') 
    87             itemText = option_text[0] + ': ' + option_text[1] 
    88             self.optionItem = QtGui.QTreeWidgetItem() 
    89             self.optionItem.setText(0, itemText) 
    90             self.parentItem.addChild(self.optionItem) 
    91  
    92         elif (type_ == "ENDOPTION"): 
    93             self.inOption = False 
    94  
    95     def insertItem(self, item): 
    96         """ 
    97         Insert an item at the correct place in the dialog tree 
    98         @type item: QtGui.QTreeWidgetItem 
    99         @param item: the item to insert 
    100         @return: None 
    101         """ 
    102         if (self.parentItem == None): 
    103             self.insertTopLevelItem(self.topLevelItemCount(), item) 
    104         else: 
    105             self.parentItem.addChild(item) 
    106  
    107     def clear(self): 
    108         """ 
    109         Clear the dialog map 
    110         """ 
    111         self.setHeaderLabel("") 
    112         self.invisibleRootItem().takeChildren() 
     89        self.map = DialogScene(main_edit) 
     90        self.setScene(self.map.scene) 
     91        self.setSceneRect(0,0,int(settings.res_width),int(settings.res_height)) 
     92        self.setGeometry(QtCore.QRect(0,0,int(settings.res_width),int(settings.res_height))) 
  • trunk/tools/writing_editor/scripts/parser.py

    r251 r280  
    1616#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>. 
    1717 
    18 import re 
    19 from lib.pyparsing import * 
    2018from PyQt4 import QtGui, QtCore 
    2119 
    22 class Parser(): 
     20# goto_table stores label's so you can jump back to them 
     21# they are stored in the format: {label (string): paragraph number (int, starts at 0)} 
     22goto_table = {} 
     23 
     24class ConversationNode(object): 
    2325    """ 
    24     The parser 
     26    The class for every node in the conversation tree 
    2527    """ 
    26     def __init__(self, widget, result_function): 
     28    def __init__(self, label="", text="", type_="", options=[]): 
    2729        """ 
    28         Initialize the parser 
    29         @type document: QtGui.QTextEdit 
    30         @param document: The QTextEdit 
    31         @type result_function: function 
    32         @param result_function: the function that handles the results 
     30        Initialize the node 
     31        @type label: string 
     32        @param label: the label for the node 
     33        @type text: string 
     34        @param text: the text for the node 
     35        @type type_: string 
     36        @param type_: the type of node, either 'text' or 'option' 
     37        @type options: list 
     38        @param options: a list of all the option nodes 
    3339        @return: None 
    3440        """ 
    35         self.widget = widget 
    36         self.resultFunction = result_function 
    37         self.makeFunctions() 
    38         self.funcs = ["SCRIPTNAME", "NPC", "CALLSECTION", "ENDSECTION", "SECTION",  
    39                       "SCRIPTNAME", "ENDOPTION", "OPTION", "PLAYSOUND",  
    40                       "SAY", "ATTACK", "RETURN", "ELIF", "IF", "ELSE", "PC", "."] 
    41  
    42         self.func_by_name = {"NPC":self.npc, "PC":self.pc, "CALLSECTION":self.callsection, 
    43                              "ENDSECTION":self.endsection, "SECTION": self.section, 
    44                              "SCRIPTNAME":self.scriptname, "ENDOPTION":self.endoption, 
    45                              "OPTION":self.option, "PLAYSOUND":self.playsound, 
    46                              "SAY":self.say, "ATTACK":self.attack, "RETURN":self.return_, 
    47                              "ELIF":self.elif_, "IF":self.if_, "ELSE":self.else_, 
    48                              ".":self.option_item} 
     41        self.label = label 
     42        self.text = text 
     43        self.type_ = type_ 
     44        self.options = options 
    4945 
    5046 
    51     def makeFunctions(self): 
    52         """ 
    53         Setup all the matching functions 
    54         @return: None 
    55         """ 
    56         self.text = Word(alphanums) 
    57         self.nums = Word(nums) 
    58         self.period = Literal(".") 
    59         self.space = Literal(" ") 
    60         self.colon = Literal(":") 
    61         self.quote = Literal("\"") 
     47def parse(text): 
     48    """ 
     49    Parse the text and create the tree structure from it 
     50    @type text: string 
     51    @param text: the text to parse 
     52    @return: the root node 
     53    """ 
     54    text = str(text).strip() 
    6255 
    63         self.npc = Combine(Word("NPC") + self.space + self.text) 
    64         self.pc = Word("PC") 
    65         self.section = Combine(Word("SECTION") + self.space + self.text) 
    66         self.endsection = Word("ENDSECTION") 
    67         self.callsection = Combine(Word("CALLSECTION") + self.space + self.text) 
    68         self.scriptname = Combine(Word("SCRIPTNAME") + self.space + self.text) 
    69         self.option = Combine(Word("OPTION") + self.space + self.text) 
    70         self.option_item = Combine(self.nums + Optional(self.space) + self.period + Optional(self.space) + self.text) 
    71         self.endoption = Combine(Word("ENDOPTION") + self.space + self.text) 
    72         self.playsound = Combine(Word("PLAYSOUND") + self.space + self.quote + self.text + self.quote) 
    73         self.say = Combine(self.text + self.space + Word("SAY") + self.space + self.quote  
    74                            + self.text + self.quote) 
    75         self.attack = Combine(self.text + self.space + Word("ATTACK") + self.space + self.text) 
    76         self.return_ = Combine(Word("RETURN") + self.space + self.text) 
    77         self.if_ = Combine(Word("IF") + self.space + self.text + self.colon) 
    78         self.elif_ = Combine(Word("ELIF") + self.space + self.text + self.colon) 
    79         self.else_ = Combine(Word("ELSE") + self.colon) 
     56    if (text == ""): 
     57        # if there is no text return an empty node 
     58        return ConversationNode() 
    8059 
    81          
    82     def findType(self, string): 
    83         """ 
    84         Find the type of command that is in the string given 
    85         @type string: string 
    86         @param string: the string to find the type of 
    87         @return: the type 
    88         """ 
    89         type_ = None 
    90         for func in self.funcs: 
    91             regex = re.compile(func + "{0,1}") 
    92             if (regex.search(str(string)) != None): 
    93                 type_ = func 
    94                 break 
     60    # split the text into paragraphs 
     61    paras = [] 
     62    for line in text.split('\n'): 
     63        line = line.strip() 
     64        if line != "": 
     65            paras.append(line)  
     66             
     67    # if there is a label make sure to have it in the same paragraph as it's text 
     68    for para in paras: 
     69        if para.startswith("LABEL"): 
     70            para_spot = paras.index(para) 
     71            next_para = paras[para_spot+1] 
     72            new_para = para + '\n' + next_para 
    9573 
    96         return type_ 
     74            paras.remove(para) 
     75            paras.remove(next_para) 
     76            paras.insert(para_spot, new_para) 
    9777 
    98     def parse(self): 
    99         """ 
    100         Parse the text 
    101         @return: the parsed text 
    102         """         
    103         doc = self.widget.document().toPlainText() 
    104         if (doc == ""): 
    105             return 
     78    # recursively parse the text and get the root node and end 
     79    root, end = _parse(paras, 0, len(paras)-1) 
     80     
     81    # if the end is not the end of the paragraphs, then throw a syntax error 
     82    if (end != len(paras)): 
     83        print "syntax error 1" 
    10684 
    107         for line in doc.split('\n'): 
    108             if (line == ""): 
    109                 continue 
    110  
    111             line_type = self.findType(line) 
    112             try: 
    113                 command = self.func_by_name[line_type] 
    114             except KeyError, e: 
    115                 self.createErrorBox(e) 
    116                 return 
    117  
    118             parse = command.scanString(line) 
    119             for result in parse: 
    120                 self.resultFunction(result[0][0], line_type) 
     85    # else return the root node 
     86    else: 
     87        return root 
    12188 
    12289 
    123     def createErrorBox(self, error): 
    124         """ 
    125         Create an error box saying that the text couldn't be parsed 
    126         @type error: KeyError 
    127         @param error: The error that was generated 
    128         @return: None 
    129         """ 
    130         msg_box = QtGui.QMessageBox() 
    131         msg_box.setText("Error while parsing") 
    132         msg_box.setInformativeText("Could not find the type \"%s\" in self.func_by_name" % str(error)) 
    133         msg_box.setStandardButtons(QtGui.QMessageBox.Ok) 
    134         msg_box.setWindowTitle("Error") 
    135         msg_box.setWindowIcon(QtGui.QIcon("data/images/error.png")) 
    136          
    137         ret = msg_box.exec_() 
    138         if (ret == QtGui.QMessageBox.Ok): 
    139             msg_box.close() 
     90def _parse(paras, start, end): 
     91    """ 
     92    Recursively parse the text 
     93    @type paras: list 
     94    @param paras: a list of the paragraphs in the text 
     95    @type start: int 
     96    @param start: the start of the text 
     97    @type end: int 
     98    @param end: the end of the text 
     99    @return: the root node and end of text 
     100    """ 
     101    # create a new node 
     102    node = ConversationNode() 
     103 
     104    # check for a label and extract it if its there 
     105    if (paras[0].startswith("LABEL")): 
     106        label_text = paras[0].split('\n')[0][6:] 
     107        node.label = label_text 
     108        goto_table[label_text] = 0 
     109        paras[0] = paras[0].split('\n')[1] 
     110 
     111    for i in xrange(start, end): 
     112        # if the paragraph is an option, break the loop 
     113        if (paras[i].startswith("OPTION")): 
     114            break 
     115 
     116        # if the paragraph is an endoption, exit the function and return the node and the end point 
     117        elif (paras[i].startswith("ENDOPTION")): 
     118            return node, i 
     119 
     120        # else parse the text normally 
     121        else: 
     122            node.type = 'text' 
     123            node.text = paras[i] 
     124             
     125    # while not at the end of the text 
     126    while i < end: 
     127        # if the paragraph is an option, start the recursive parsing and append the option to the root node 
     128        if (paras[i].startswith("OPTION")): 
     129            txt = paras[i][7:] 
     130            child, i = _parse(paras, i+1, end) 
     131            child.type_ = 'option' 
     132            child.text = txt 
     133            node.options.append(child) 
     134             
     135        # if the paragraph is an endoption, it is a syntax error 
     136        elif (paras[i].startswith("ENDOPTION")): 
     137            print "syntax error 2" 
     138 
     139        # move to the next paragraph 
     140        i += 1 
     141 
     142    return node, i 
  • trunk/tools/writing_editor/scripts/writingEditor.py

    r251 r280  
    2424from scripts.settings import Settings 
    2525from scripts.dialogMap import DialogMap 
     26from scripts.parser import parse 
     27 
    2628 
    2729class WritingEditor(QtGui.QMainWindow): 
     
    4143        self.ui = Ui_writingEditor() 
    4244        self.ui.setupUi(self) 
    43         self.dialogMap = DialogMap(self.settings, self.ui.main_edit, self.ui.dialog_map_tab) 
     45        self.ui.dialog_map = DialogMap(self.settings, self.ui.main_edit, self.ui.dialog_map_tab) 
    4446        self.syntax = SyntaxHighlighter(self.ui.main_edit.document()) 
    4547        self.syntaxCreated = True 
     
    194196        # it's the main editor 
    195197        if (index == 0): 
    196             self.dialogMap.clear() 
    197198            self.ui.actionCopy.setEnabled(True) 
    198199            self.ui.actionCut.setEnabled(True) 
     
    200201        # it's the dialog map 
    201202        elif (index == 1): 
    202             self.dialogMap.parser.parse() 
     203            self.ui.dialog_map.map.refreshMap() 
    203204            self.ui.actionCopy.setEnabled(False) 
    204205            self.ui.actionCut.setEnabled(False) 
     
    512513        return last_slash 
    513514 
     515    def resizeEvent(self, event): 
     516        """ 
     517        Overrides the normal resize event so it will also resize the widgets within 
     518        @type event: QResizeEvent 
     519        @param event: the event (it's provided by the Qt system) 
     520        @return: None 
     521        """ 
     522        self.ui.main_edit.setGeometry(QtCore.QRect(0, 0, event.size().width(), 
     523                                                   event.size().height()-73)) 
     524        self.ui.dialog_map.setGeometry(QtCore.QRect(0, 0, event.size().width(), 
     525                                                    event.size().height()-73)) 
     526 
    514527    def closeEvent(self, event): 
    515528        """ 
Note: See TracChangeset for help on using the changeset viewer.