Commit dca011c9 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Add warning if user wants to save with syntax errors

parent 2871eaac
......@@ -56,7 +56,7 @@ from PySide.QtCore import Qt, QPoint, QPointF, QRect, QFile, QObject, Property
from PySide.QtGui import(QGraphicsPathItem, QGraphicsPolygonItem, QPainterPath,
QGraphicsItem, QPen, QColor, QMenu, QFileDialog,
QPainter, QLineEdit, QTextBlockFormat)
QPainter, QLineEdit, QTextBlockFormat, QPolygonF)
from PySide.QtUiTools import QUiLoader
......@@ -868,7 +868,7 @@ class Cornergrabber(QGraphicsPolygonItem, object):
self.prepareGeometryChange()
rect = self.parent.boundingRect()
self.setPos(0, 0)
self.setPolygon(rect)
self.setPolygon(QPolygonF(rect))
self.show()
def mousePressEvent(self, event):
......
......@@ -335,65 +335,77 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
# Keep a track of highlighted symbols: { symbol: brush }
self.highlighted = {}
@property
def visible_symb(self):
''' Return the visible items of a scene '''
return (it for it in self.items() if it.isVisible() and
isinstance(it, Symbol))
@property
def editable_texts(self):
''' Return all EditableText areas of a scene '''
return (it for it in self.items() if it.isVisible() and
isinstance(it, EditableText))
@property
def floating_symb(self):
''' Return the top level floating items of a scene '''
return (it for it in self.visible_symb if not it.hasParent)
@property
def processes(self):
''' Return visible processes components of the scene '''
return (it for it in self.visible_symb if isinstance(it, Process) and
not isinstance(it, Procedure))
@property
def procedures(self):
''' Return visible procedures components of the scene '''
return (it for it in self.visible_symb if isinstance(it, Procedure))
@property
def states(self):
''' Return visible state components of the scene '''
return (it for it in self.visible_symb if isinstance(it, State))
@property
def texts(self):
''' Return visible text areas components of the scene '''
return (it for it in self.visible_symb if isinstance(it, TextSymbol))
@property
def procs(self):
''' Return visible procedure declaration components of the scene '''
return (it for it in self.visible_symb if isinstance(it, Procedure))
@property
def start(self):
''' Return visible start components of the scene '''
return (it for it in self.visible_symb if isinstance(it, Start))
@property
def floating_labels(self):
''' Return visible floating label components of the scene '''
return (it for it in self.floating_symb if isinstance(it, Label))
@property
def returns(self):
''' Return visible return components of the scene '''
return (it for it in self.visible_symb if isinstance(it,
ProcedureStop))
@property
def composite_states(self):
''' Return states that contain a composite part '''
......@@ -405,11 +417,13 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
each.nested_scene
return self._composite_states
@composite_states.setter
def composite_states(self, value):
''' Attribute setter '''
self._composite_states = value
@property
def all_nested_scenes(self):
''' Return all nested scenes, recursively '''
......@@ -419,10 +433,12 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
for sub in each.nested_scene.all_nested_scenes:
yield sub
def quit_scene(self):
''' Called in case of scene switch (e.g. UP button) '''
pass
def render_everything(self, ast):
''' Render a process and its children scenes, recursively '''
already_created = []
......@@ -509,6 +525,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
recursive_render(ast, self)
def refresh(self):
''' Refresh the symbols and connections in the scene '''
for symbol in self.visible_symb:
......@@ -539,6 +556,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
for symbol in self.visible_symb:
symbol.update_connections()
def set_cursor(self, follower):
''' Set the cursor shape depending on the selected menu item '''
for item in self.items():
......@@ -551,6 +569,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
# if there are items not having allowed_followers
pass
def reset_cursor(self):
''' Reset the default cursor of an item '''
for item in self.items():
......@@ -559,6 +578,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
except AttributeError:
pass
def translate_to_origin(self):
'''
Translate all items to coordinate system starting at (0,0),
......@@ -577,6 +597,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
item.pos_y += delta_y
return delta_x, delta_y
def selected_symbols(self):
''' Generate the list of selected symbols (excluding grabbers) '''
for selection in self.selectedItems():
......@@ -585,16 +606,26 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
elif isinstance(selection, Cornergrabber):
yield selection.parent
def set_selection(self, toolbar):
''' When the selection has changed, update menu, etc '''
toolbar.update_menu(self)
for item in self.selected_symbols():
item.grabber.display()
def raise_syntax_errors(self, errors=None):
''' Display an syntax error pop-up message '''
def syntax_errors(self, symb):
''' Parse a symbol and return a list of syntax errors '''
return symb.check_syntax('\n'.join(Pr.generate(symb, recursive=False)))
def check_syntax(self, symbol):
''' Check syntax of a symbol and display a pop-up in case of errors '''
errors = self.syntax_errors(symbol)
if not errors:
return
for view in self.views():
errs = []
for error in errors:
......@@ -619,11 +650,18 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
msg_box.setDefaultButton(QtGui.QMessageBox.Discard)
msg_box.exec_()
def check_syntax(self, symbol):
''' Create PR representation for a symbol and check its syntax '''
pr_text = '\n'.join(Pr.generate(symbol, recursive=False))
errors = symbol.check_syntax(pr_text)
self.raise_syntax_errors(errors)
def global_syntax_check(self):
''' Parse each visible symbol in the current scene and its children
and check syntax using the parser '''
errors = []
for each in self.visible_symb:
err = self.syntax_errors(each)
errors.extend(err)
if errors:
return False
return True
def update_completion_list(self, symbol):
''' When text has changed on a symbol, update the data dictionnary '''
......@@ -632,6 +670,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
nextstate=False, cpy=True))
symbol.update_completion_list(pr_text=pr_text)
def highlight(self, item):
''' Highlight a symbol '''
if item in self.highlighted:
......@@ -653,6 +692,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
item.setBrush(brush)
self.highlighted = {}
def find_text(self, pattern):
''' Return all symbols with matching text '''
for item in (symbol for symbol in self.items()
......@@ -661,6 +701,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
if re.search(pattern, unicode(item), flags=re.IGNORECASE):
yield item.parentItem()
def search(self, pattern, replace_with=None):
''' Search and replace function ; get next search result with key n '''
self.clearSelection()
......@@ -690,6 +731,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
except StopIteration:
LOG.info('Pattern not found')
def delete_selected_symbols(self):
'''
Remove selected symbols from the scene, with proper re-connections
......@@ -709,6 +751,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
pass
self.undo_stack.endMacro()
def copy_selected_symbols(self):
'''
Create a copy of selected symbols to a buffer (in AST form)
......@@ -724,6 +767,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
LOG.error(unicode(error_msg))
raise
def cut_selected_symbols(self):
'''
Create a copy of selected symbols, then delete them
......@@ -735,6 +779,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
else:
self.delete_selected_symbols()
def paste_symbols(self):
'''
Paste previously copied symbols at selection point
......@@ -775,6 +820,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
self.undo_stack.endMacro()
self.refresh()
def sdl_to_statechart(self, basic=False):
''' Create a graphviz representation of the SDL model '''
pr_raw = Pr.parse_scene(self)
......@@ -789,6 +835,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
Helper.flatten(process_ast)
return Statechart.create_dot_graph(process_ast, basic)
def export_branch_to_picture(self, symbol, filename, doc_format):
''' Save a symbol and its followers to a file '''
temp_scene = SDL_Scene(context=self.context)
......@@ -802,6 +849,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
except AttributeError:
pass
def export_img(self, filename=None, doc_format='png', split=False):
''' Save the scene as a PNG/SVG or PDF document
If specified, split the diagram in multiple files, one
......@@ -870,6 +918,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
if painter.isActive():
painter.end()
def clear_focus(self):
''' Clear focus from any item on the scene '''
try:
......@@ -878,6 +927,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
# if no focus item
pass
def symbol_near(self, pos, dist=5, selectable_only=True):
''' If any, returns symbol around pos '''
items = self.items(
......@@ -888,6 +938,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
or not selectable_only):
return item.parent if isinstance(item, Cornergrabber) else item
def can_insert(self, pos, item_type):
''' Check if we can add an item type at a given position '''
parent_item = self.symbol_near(pos)
......@@ -906,6 +957,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
' symbol cannot be followed by ' +
item_type.__name__)
# pylint: disable=C0103
def mousePressEvent(self, event):
'''
......@@ -964,6 +1016,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
# if not OK, reset and:
self.mode = 'idle'
# pylint: disable=C0103
def mouseMoveEvent(self, event):
''' Handle Click + Mouse move, based on the mode '''
......@@ -976,6 +1029,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
# Update the line
pass
# pylint: disable=C0103
def mouseReleaseEvent(self, event):
if self.mode == 'select_items':
......@@ -991,6 +1045,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
self.mode = 'idle'
super(SDL_Scene, self).mouseReleaseEvent(event)
# pylint: disable=C0103
def keyPressEvent(self, event):
''' Handle keyboard: Delete, Undo/Redo '''
......@@ -1060,6 +1115,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
pprint.pprint(selection.__dict__, None, 2, 1)
code.interact('type your command:', local=locals())
def create_subscene(self, context):
''' Create a new SDL scene, e.g. for nested symbols '''
subscene = SDL_Scene(context=context)
......@@ -1067,6 +1123,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
subscene.parent_scene = self
return subscene
def place_symbol(self, item_type, parent, pos=None):
''' Draw a symbol on the scene '''
item = item_type()
......@@ -1101,6 +1158,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
view.ensureVisible(item)
return item
def add_symbol(self, item_type):
''' Add a symbol, or postpone until a parent symbol is selected '''
try:
......@@ -1421,11 +1479,39 @@ class SDL_View(QtGui.QGraphicsView, object):
self, "Save model", ".", "SDL Model (*.pr)")[0]
if self.filename and self.filename.split('.')[-1] != 'pr':
self.filename += ".pr"
filename = (
(self.filename or '_opengeode')
+ '.autosave') if autosave else self.filename
filename = ((self.filename or '_opengeode')
+ '.autosave') if autosave else self.filename
# If the current scene is a nested one, save the top parent
if self.parent_scene:
scene = self.parent_scene[0][0]
else:
scene = self.scene()
if not scene:
LOG.info('No scene - nothing to save')
return False
# check syntax and raise a big warning before saving
if not scene.global_syntax_check():
LOG.error('Syntax errors must be fixed NOW '
'or you may not be able to reload the model')
msg_box = QtGui.QMessageBox(self)
msg_box.setIcon(QtGui.QMessageBox.Critical)
msg_box.setWindowTitle('OpenGEODE - Syntax Error')
#msg_box.setInformativeText('\n'.join(errs))
msg_box.setText("Syntax errors were found. It is not advised to "
"save the model now, as you may not be able to "
"open it again. Are you sure you want to save?")
msg_box.setStandardButtons(QtGui.QMessageBox.Save
| QtGui.QMessageBox.Cancel)
res = msg_box.exec_()
if res == QtGui.QMessageBox.Cancel:
return False
if not filename and not autosave:
return False
else:
pr_file = QFile(filename)
pr_file.open(QIODevice.WriteOnly | QIODevice.Text)
......@@ -1434,15 +1520,6 @@ class SDL_View(QtGui.QGraphicsView, object):
.split(os.path.extsep)[0:-1]).split(os.path.sep)[-1]
self.wrapping_window.setWindowTitle(
'process ' + self.scene().process_name + '[*]')
# If the current scene is a nested one, save the top parent
if self.parent_scene:
scene = self.parent_scene[0][0]
else:
scene = self.scene()
if not scene:
LOG.info('No scene - nothing to save')
return False
# Translate all scenes to avoid negative coordinates
delta_x, delta_y = scene.translate_to_origin()
......@@ -1590,6 +1667,7 @@ class SDL_View(QtGui.QGraphicsView, object):
self.set_toolbar()
return True
def log_errors(self, errors, warnings):
''' Report Error and Warnings on the console and in the log window '''
if self.messages_window:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment