def __init__(self):

        db = Database('database')

        if db .root is None:

            root = Persistent()
            root.list_of_users = []
            db.elect (root)
            transaction.commit()

            checkout(db.root)

            self.list_of_users = db.root.list_of_users

            # Creating a user default to make easy the development
            user = User('Fulano', '*****@*****.**', 'pwd', "uid")

            document = Document("My document", ".txt", "I think to myself... what a wonderful world!", "uid", user.folder.id)
            document.id = "did"
            user.folder.id = "fid"
            user.add_document(user.folder.id, document)

            db.root.list_of_users.append(user)
            db.root.list_of_users = self.list_of_users

            transaction.commit()

            checkout(db .root)

            self.list_of_users = db .root.list_of_users
        else:
            self.list_of_users = db.root.list_of_users
        super(UserRepository, self).__init__()
    def create(self):
        try:
            category = self.params.get("category")
            doc = Document(name=self.params["name"], category_id=category, document_id=self.params.get("document_id"))
            doc.put()

            self.redirect("/site/document/")
        except Exception, ex:
            self.render(text="Exception: %s" % ex)
Exemple #3
0
 def __init__(self, fileName):
     self._fN=fileName
     self._doc=Document()
     self._numStaples=0
     self._scaffoldLength=0
     decode(self._doc,file(self._fN).read())
     self.getModelInfo()
     self.printModelInfo()
     self.vhLoop()
     self.numberScaffold()
Exemple #4
0
def read_articles(path):
    articles = []
    cnt = 0

    for filename in os.listdir(path):
        with open(path + '/' + filename) as file:
            doc = Document(cnt, file.read())
            doc.words, doc.sentences = process_document(doc.raw_text)
            count_tfidf(doc.sentences, doc.words)
            doc.graph = create_graph(doc.sentences, doc.words)
            cnt = cnt + 1
            articles.append(doc)

    return articles
Exemple #5
0
    def new(self):
	query = db.GqlQuery("SELECT * FROM Page ORDER BY order DESC")
	self.page_num = 1
	if query.count() > 0:
	  page = query.get()
	  self.page_num = page.order + 1
        self.document = Document.get(self.params.get('id'))
 def delete(self):
     key = self.params.get("key")
     res = {"status": "success", "msg": "削除しました"}
     document = Document.get(key)
     if document:
         document.delete()
     else:
         res = {"status": "error", "msg": "削除に失敗しました"}
     self.render(json=self.to_json(res))
def create_document():
    if 'project' in request.json:
        project = request.json['project']
    else:
        response = {'message': "Missing project"}
        response = make_response(response)
        return response, 400
    if 'content' in request.json:
        content = request.json['content']
    else:
        response = {'message': "Missing content"}
        response = make_response(response)
        return response, 400

    doc = Document(content, [], [])
    doc.data = content
    doc.upload(project)
    return '', 204
Exemple #8
0
 def load_collection(self):
     documents = []
     for doc in la('docs/'):
         lines = open('docs/' + doc, 'r').readlines()
         bow = []
         for line in lines:
             bow += self.lineToarray(line)
         d = Document(doc, bow)
         documents.append(d)
     return documents
Exemple #9
0
 def searchDocuments(self):
     QApplication.instance().doclist.show()
     result = self.search.text()
     QApplication.instance().currentWord = result
     self.clearResults()
     documents = []
     if result == "":
         for filepath in self.indexer.data['FILES']:
             QApplication.instance().doclist.addItem(Document(filepath, os.path.basename(filepath)))
             self.parent.leftDock.show()
     else:
         try:
             documentSet = parseResult(result)
             for filepath in documentSet:
                 documents.append(Document(filepath, os.path.basename(filepath), result))
             documents.sort(key=lambda x: x.rank, reverse=True)
             for doc in documents:
                 QApplication.instance().doclist.addItem(doc)
             self.parent.leftDock.show()
         except ValueError:
             pass
Exemple #10
0
    def _create_item(self, metadata, parent):
        if metadata["Type"] == "CollectionType":
            new_object = Collection(metadata, parent)

        elif metadata["Type"] == "DocumentType":
            new_object = Document(metadata, parent)

        else:
            raise Exception("Unknown type %s" % metadata["Type"])

        parent.add_child(new_object)
        return new_object
Exemple #11
0
    def create(self):
     #try:
        document = Document.get(self.params.get('document'))
        page = Page(  name=self.params.get('name'),
                      document=document,
                      content=self.params.get('content'),
                      order=int(self.params.get('order'))
              )
        # データの保存
        page.put()

        # URL '/page/' にリダイレクト
        self.redirect('/document/edit/'+self.params.get('document') )
Exemple #12
0
def create_document(project_name):
    id_token = request.args.get('id_token')

    if id_token is None or id_token == "":
        response = {
            'message': "ID Token is not included with the request uri in args"
        }
        response = make_response(response)
        return response, 400

    requestor_email = get_email(id_token)

    if requestor_email is None:
        response = {'message': "ID Token has expired or is invalid"}
        response = make_response(response)
        return response, 400

    users_col = get_col(project_name, "users")
    requestor = users_col.find_one({
        'email': requestor_email,
        'isContributor': True
    })
    if requestor is None:
        response = {'message': "You are not authorised to perform this action"}
        response = make_response(response)
        return response, 403

    if 'content' in request.json:
        content = request.json['content']
    else:
        response = {'message': "Missing content"}
        response = make_response(response)
        return response, 400

    doc = Document(content, [], [])
    doc.data = content
    doc.upload(project_name)
    return '', 204
Exemple #13
0
def upload():
    dico = {}
    dico['metadata'] = {}
    output_dir = os.path.join(os.path.join(os.getcwd(), 'app'), 'output_dir/')
    if request.method == 'POST':
        file = request.files['file']
        f_name = request.files["file"].filename
        doc = Document(file, f_name)
        _data = doc.refersTo()

        if 'error' not in list(_data.keys()):
            content = ""
            for key, value in _data.items():
                if key != "content":
                    dico['metadata'][key] = value
                else:
                    dico[key] = value
            dico['metadata']['mime_type'] = request.files["file"].content_type
        else:
            resp = jsonify({'message': _data['error']})
            resp.status_code = 400
            return resp
        try:
            with open(
                    os.path.join(output_dir,
                                 f_name.split('.')[0]) + '.json',
                    'w+') as outfile:
                json.dump(dico, outfile)
        except:
            print("Can't write json")

        return jsonify(dico)
    else:
        resp = jsonify(
            {'message': 'Cette méthode ne peut être exécuté que par un POST'})
        resp.status_code = 405
        return resp
def upload_file():
    if request.method == 'POST':

        if 'projectName' in request.form:
            project_name = str(request.form['projectName'])

            if 'inputFile' not in request.files:
                response = {'status_code': 400, 'message': 'No file selected'}
                response = make_response(response)
                return response

            file = request.files['inputFile']
            if file.filename == '':
                response = {'status_code': 400, 'message': 'No file selected'}
                response = make_response(response)
                return response
            if file:
                filename = secure_filename(file.filename)
                filelocation = os.path.join(uploads_dir, filename)
                file.save(filelocation)

                with open(filelocation) as csv_file:
                    csv_reader = csv.reader(csv_file, delimiter=",")
                    is_first_line = True

                    for row in csv_reader:
                        if is_first_line:
                            is_first_line = False
                        else:
                            document = Document(ObjectId(row[0]), [], row[1])
                            # Find project database and populate document collection
                            project = Project(project_name, [], [])
                            project.add_document(document)

                # Delete file when done
                os.remove(filelocation)

                response = {
                    'status_code': 200,
                    'message': 'Documents imported successfully'
                }
                return make_response(response)
        else:
            response = {
                'status_code': 400,
                'message': 'No project id provided'
            }
            response = make_response(response)
            return response
    def __init__(self):
        """docstring for __init__"""
        # initialize variables
        self._document = Document()
        self._document.setController(self)
        self._activePart = None
        self._filename = None
        self._fileOpenPath = None  # will be set in _readSettings
        self._hasNoAssociatedFile = True
        self._pathViewInstance = None
        self._sliceViewInstance = None
        self._undoStack = None
        self.win = None
        self.fileopendialog = None
        self.filesavedialog = None

        self.settings = QSettings()
        self._readSettings()

        # call other init methods
        self._initWindow()
        if app().isInMaya():
            self._initMaya()
        app().documentControllers.add(self)
Exemple #16
0
 def __init__(self, doc=None, fname=None):
     """docstring for __init__"""
     # initialize variables
     self._document = Document() if not doc else doc
     self._filename = fname
     self.win = None
     self._undoStack = None
     self._hasNoAssociatedFile = None
     self._sliceViewInstance = None
     self._pathViewInstance = None
     self._solidView = None
     self._activePart = None
     # call other init methods
     self.initWindow()
     self.initMaya()
     app().documentControllers.add(self)
Exemple #17
0
def main():
    logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)

    file_stream = FileStream(
        r"D:\Eigene Dateien\git\pandaLight-HDL"
        r"\pandaLight-tests\complete LED test\PANDA_LIGHT.vhd")

    lexer = VhdlLexer(file_stream)
    token_stream = CommonTokenStream(lexer)
    parser = VhdlParser(token_stream)

    listener = VhdlListener()
    tree = parser.design_file()

    walker = ParseTreeWalker()
    walker.walk(listener, tree)

    print(Document.from_tree(tree).tree_string())
    def update(self):
        doc = Document.get(self.params.get("key"))

        published = self.params.get("published")
        if published != None and published == "True":
            doc.published = True
        else:
            doc.published = False

        doc.name = self.params.get("name")
        doc.categoey_id = self.params.get("category_id")
        c = db.GqlQuery("SELECT * FROM Category WHERE category_id = :1", doc.category_id).get()
        doc.category_name = c.name
        doc.content = self.params.get("content")
        doc.document_id = self.params.get("document_id")
        doc.put()

        self.redirect("/document/edit/" + self.params.get("key"))
Exemple #19
0
def to_documents(lines):
    documents = []
    for l in lines:
        a = l.strip().split(' ')
        num_terms = int(a[0])  # number of unique terms
        terms = []
        counts = []
        num_words = 0
        # Add word to doc
        for t in a[1:]:
            b = t.split(':')
            w = int(b[0])  # term
            n_w = int(b[1])  # number of occurrence
            terms.append(w)
            counts.append(n_w)
            num_words += n_w
        # Add doc to corpus
        doc = Document(num_terms, num_words, terms, counts)
        documents.append(doc)
    return documents
    def __init__(self):
        """docstring for __init__"""
        # initialize variables
        self._document = Document()
        self._document.setController(self)
        self._activePart = None
        self._filename = None
        self._fileOpenPath = None  # will be set in _readSettings
        self._hasNoAssociatedFile = True
        self._pathViewInstance = None
        self._sliceViewInstance = None
        self._undoStack = None
        self.win = None
        self.fileopendialog = None
        self.filesavedialog = None

        self.settings = QSettings()
        self._readSettings()

        # call other init methods
        self._initWindow()
        if app().isInMaya():
            self._initMaya()
        app().documentControllers.add(self)
    def __init__(self, firstuse):         
        app().documentControllers.add(self)
        self.doc = Document()
        self.idbank = IdBank()
        self.undoStack = QUndoStack()
        app().undoGroup.addStack(self.undoStack)
        self.win = DocumentWindow(docCtrlr=self)
        self.win.show()
        self.stdwin = StdWindow()
        self.webwin = WebWindow()
        self._filename = "untitled.cadnano"
        self.treeController = TreeController(self.win.treeview)
        self.createConnections()
        
        self.recentButton = None
        
        self.win.sliceGraphicsView.mouseMoveEvent = self.sliceViewMouseMove

        if firstuse == True:
            self.win.helplabel.setText(QString("It appears as though this is your first time using CaDNAno 2, would you like to view a tutorial?"))
        else:
            self.win.ignoreButton.hide()
            self.win.showHelpButton.hide()
            self.win.helplabel.hide()
Exemple #22
0
class analysisHelpers(object):
    def __init__(self, fileName):
        self._fN=fileName
        self._doc=Document()
        self._numStaples=0
        self._scaffoldLength=0
        decode(self._doc,file(self._fN).read())
        self.getModelInfo()
        self.printModelInfo()
        self.vhLoop()
        self.numberScaffold()
	
    ##Initialization Functions
    def vhLoop(self):
    	for vh in self._vhList:
    	    vh._renumber=-1
    	    vh._neighbors=self._part.getVirtualHelixNeighbors(vh)
    
    def getModelInfo(self):
		self._part=self._doc.selectedPart()
		self._vhList=self._part.getVirtualHelices()
		self._oligos=list(self._part.oligos())
	
    def printModelInfo(self):
		self._numVH=len(self._vhList)
		self._numOligos=len(self._oligos)
		print "Design with %s virtual helices and %s oligos loaded" %(self._numVH,self._numOligos)
	
    #Sequence Application Functions	
    def readSeqFile(self,sequenceFile):
		sqFile=file(sequenceFile)
		self._fullSeq=sqFile.read().strip()
		self._fullSeqLength=len(self._fullSeq)
		print "Sequence length %s" %(self._fullSeqLength)
	
    def applySequenceToScaffold(self, sequenceFile):
		self.readSeqFile(sequenceFile)
		#Find scaffold
		self.findScaffold()
		self._scaffold.applySequence(self._fullSeq)
		print "Applied sequence with %s length\n" %(len(self._scaffold.sequence()))
		self._scaffoldLength=len(self._scaffold.sequence())
        
    def findScaffold(self):
		for x in self._oligos:
			scaf=x.isStaple()
			if scaf==False:
				self._scaffold=x
                #print "scaffold found"
    
    def getCircleMapData(self):
        self.numberScaffold()
        self.numberStaples()
        self.printCircleData()

    
    def numberScaffold(self):
        #Get start info
        self.findScaffold()
        count=1
        first=0
        for strand in self._scaffold.strand5p().generator3pStrand():
            sLowIdx, sHighIdx = strand._baseIdxLow, strand._baseIdxHigh
            subn=strand.insertionLengthBetweenIdxs(sLowIdx, sHighIdx)
            difference=strand.length()+subn
            vh=strand.virtualHelix()
            if vh._renumber==-1:
                #print "Renumbering helix %s as %s\n" %(vh.number(), first)
            	vh._renumber=first
            	first=first+1
            #print "length is %s" %difference
            count+=(difference)
            idx5p=strand.idx5Prime()
            idx3p=strand.idx3Prime()
            #if strand.isDrawn5to3
            
            strand.num5Prime=count-difference
            strand.num3Prime=count-1
            if not self._scaffoldLength:
            	self._scaffoldLength=strand.num3Prime
        #scaffvh=self._scaffold._strand5p.virtualHelix()
        
    def numberStaples(self):
        for x in self._oligos:
            if x.isStaple()==True:            	
                self._numStaples+=1
                for strand in x.strand5p().generator3pStrand():
                    strand.num5Prime=[]
                    strand.num3Prime=[]
                    #print "Staple part\n"
                    scafComp=strand.getComplementStrands()
                    if len(scafComp)>0:
                    	for p in range(0,len(scafComp)):
                        	dir1=scafComp[p].isDrawn5to3()
                            	diff53p=np.abs(scafComp[p].idx5Prime()-strand.idx3Prime())
                                diff35p=np.abs(scafComp[p].idx3Prime()-strand.idx5Prime())
                                strand.num5Prime.append(scafComp[p].num3Prime-diff35p)
                            	strand.num3Prime.append(scafComp[p].num5Prime+diff53p)


    def printCircleData(self):
        fileString="CD_"
        fileString+=self._fN
        fileString+=".csv"
        self._circleFileName=fileString
        self._circleFile=open(fileString,'w')
        self._circleFile.write("Staple locations on scaffold 5'->3'\n")
        for x in self._oligos:
            if x.isStaple()==True:
                stapleString=""
                count=0
                for strand in x.strand5p().generator3pStrand():
                    if count>0:
                        stapleString+=","
                    scafComp=strand.getComplementStrands()
                    if len(scafComp)>0:
                   	n=len(strand.num5Prime)
                   	for p in range(0,n):
                   		if p!=0:
                   			stapleString+=","
                   		stapleString+=("%s, %s") %(strand.num5Prime[p],strand.num3Prime[p])

                    count+=1
                if stapleString:
                    stapleString+="\n"
                    #print stapleString
                    self._circleFile.write(stapleString)
        
        self._circleFile.close()
        
    def getVirtualHelixSequences(self):      
        for vh in self._vhList:
            seq=''
            first=1
            minLoc=0
            maxLoc=0
            for scaffStr in vh.scaffoldStrandSet():
            	scaffStr._minMax=[]
            	seq+=scaffStr.sequence()
            	if minLoc==0 and maxLoc==0:
            	   minLoc=scaffStr.idx5Prime()
            	   maxLoc=scaffStr.idx3Prime()
            	if scaffStr.isDrawn5to3():
            	  if scaffStr.idx5Prime()<minLoc:
            	     minLoc=scaffStr.idx5Prime()
            	  if scaffStr.idx3Prime()>maxLoc:
            	     maxLoc=scaffStr.idx3Prime()
            	else:
            	  if scaffStr.idx3Prime()<minLoc:
            	     minLoc=scaffStr.idx3Prime()
            	  if scaffStr.idx5Prime()>maxLoc:
            	     maxLoc=scaffStr.idx5Prime()     
                scaffStr._minMax.append([minLoc,maxLoc])
            vh._minMax=[minLoc,maxLoc]
            vh._sequence=seq
        
    
    def printScaffoldLength(self):
        return self._scaffoldLength
    
    def printCircleFileName(self):
        return self._circleFileName
    
    def setScaffoldLength(self,sfL):
    	self._scaffoldLength=sfL
   
    def getVirtualHelixList(self):
   	return self._vhList
   
    def getScaffoldStrand(self):
   	return self._scaffold
    


                        
                
            
Exemple #23
0
from operator import attrgetter

from model.document import Document
from model.inverted_index import InvertedIndex

if __name__ == "__main__":
    document = []
    document.append(Document(
        "1", "Candi Prambanan merupakan salah satu candi yang ada di Indonesia"))
    document.append(
        Document("2", "Indonesia Merupakan Negara Yang Mempunyai Banyak Candi"))
    document.append(Document("3", "Liburan di Negara Berkembang"))

    # iterate doc
    doc = Document()
    for doc in document:
        print(doc.docId + " -> " + doc.content)  # menampilkan konten dokumen
    # end iterate

    print("=======")
    print("Terms : ")
    print()

    iIndex = InvertedIndex.toInvertedIndex(document)
    print(iIndex)
Exemple #24
0
    def parse(self, file_path):
        """It parses the content of file_path and extracts relevant information
        from a TempEval-3 annotated file. Those information are packed in a
        Document object, which is our internal representation.
        """
        assert os.path.isfile(file_path), 'File path does not exist!'
        logging.info('Document {}: parsing...'.format(
            os.path.relpath(file_path)))
        xml = etree.parse(file_path)
        text_node = xml.findall(".//TEXT")[0]
        text_string = etree.tostring(text_node, method='text', encoding='utf8')
        text_xml = etree.tostring(text_node, method='xml', encoding='utf8')
        text_string = unicode(text_string, 'UTF-8')
        text_xml = unicode(text_xml, 'UTF-8')
        right_chars = len(text_xml.split('</TEXT>')[1])
        text_string = text_string[:-right_chars]
        text_xml = etree.tostring(text_node)

        # StanfordParser strips internally the text :(
        left_chars = len(text_string) - len(text_string.lstrip())
        with Mute_stderr():
            stanford_tree = CORENLP.parse(text_string)

        document = Document(file_path)
        document.text_offset = left_chars
        document.file_path = os.path.abspath(file_path)
        document.doc_id = os.path.basename(file_path)
        document.sec_times = self.get_dct(file_path)
        document.dct = document.sec_times.admission_date
        document.dct_text = document.dct.replace('-', '')
        document.title = os.path.basename(file_path)
        document.text = text_string
        document._coref = stanford_tree.get('coref', [])

        for num_sen, stanford_sentence in\
                enumerate(stanford_tree['sentences']):
            collp_deps = stanford_sentence.get('collapsed_dependencies', None)
            basic_deps = stanford_sentence.get('basic_dependencies', None)
            parsetree = stanford_sentence.get('parsetree', u'')

            sentence_text = stanford_sentence.get('text', u'')

            sentence = Sentence(id_sentence=num_sen,
                                basic_dependencies=basic_deps,
                                collapsed_dependencies=collp_deps,
                                parsetree=parsetree,
                                text=sentence_text)
            for num_word, (word_form, attr) in\
                    enumerate(stanford_sentence['words']):
                offset_begin = int(attr['CharacterOffsetBegin']) - left_chars
                offset_end = int(attr['CharacterOffsetEnd']) - left_chars
                word = Word(word_form=word_form,
                            char_offset_begin=offset_begin,
                            char_offset_end=offset_end,
                            lemma=attr['Lemma'],
                            named_entity_tag=attr['NamedEntityTag'],
                            part_of_speech=attr['PartOfSpeech'],
                            id_token=num_word,
                            id_sentence=num_sen)
                sentence.words.append(word)
            document.sentences.append(sentence)

        document.gold_annotations = self._get_annotations(
            xml, document)
        document.store_gold_annotations()
        document.complete_structure()

        logging.info('Document {}: parsed.'.format(os.path.relpath(file_path)))
        return document
def save_document(document):
    d = Document(document['name'], jwt.current_user.id)
    db.session.add(d)
    db.session.commit()
    return d
Exemple #26
0
def upload_file():
    id_token = request.args.get('id_token')

    if id_token is None or id_token == "":
        response = {
            'message': "ID Token is not included with the request uri in args"
        }
        response = make_response(response)
        return response, 400

    requestor_email = get_email(id_token)

    if requestor_email is None:
        response = {'message': "ID Token has expired or is invalid"}
        response = make_response(response)
        return response, 400

    if request.method == 'POST':

        if 'projectName' in request.form:
            project_name = str(request.form['projectName'])
        else:
            response = {'message': 'No project id provided'}
            response = make_response(response)
            return response, 400

        users_col = get_col(project_name, "users")
        requestor = users_col.find_one({
            'email': requestor_email,
            'isContributor': True
        })
        if requestor is None:
            response = {
                'message': "You are not authorised to perform this action"
            }
            response = make_response(response)
            return response, 403

        if 'inputFile' not in request.files:
            response = {'message': 'No file selected'}
            response = make_response(response)
            return response, 400

        file = request.files['inputFile']

        if file.filename == '':
            response = {'message': 'No file selected'}
            response = make_response(response)
            return response, 400

        if file:
            filename = secure_filename(file.filename)
            filelocation = os.path.join(uploads_dir, filename)
            file.save(filelocation)

            with open(filelocation) as csv_file:
                csv_reader = csv.reader(csv_file, delimiter=",")
                is_first_line = True

                for row in csv_reader:
                    if is_first_line:
                        is_first_line = False
                    else:
                        document = Document(row[1], [], [])
                        # Find project database and populate document collection
                        project = Project(project_name, [], [])
                        project.add_document(document)

            # Delete file when done
            os.remove(filelocation)

            response = {'message': 'Documents imported successfully'}
            response = make_response(response)
            return response, 200
def test_setup_document():
    my_document = Document("data", [], [])
    assert (my_document.data == "data")
Exemple #28
0
 def _load_brush_icon_for_rawimage(self, brush):
     buf, w, h, stride = Document.load_image(brush.icon, 'RGB')
     brush.icon_preview = buf
     data = rawimage.mkRawimageData(w, h, buf,
                                    rawimage.RAWIMAGE_FORMAT_RAW_RGB_ID)
     return data
Exemple #29
0
from model.document import Document
from utils.DBUtils import get_db_session, array_to_bytes
from utils.InitializeUtils import initialize

if __name__ == '__main__':
    session = get_db_session()

    print("Initializing...")
    docs_df = initialize()
    print("Initialization completed!\n")

    print("Inserting documents into db...")
    for index, row in docs_df.iterrows():
        doc = Document(id=row['id'],
                       path=row['path'],
                       filename=row['filename'],
                       text=array_to_bytes(row['text']))
        print("Inserting obj:", doc)
        session.add(doc)
        session.commit()
    print("Done!")
Exemple #30
0
    def parse(self, file_path):
        """It parses the content of file_path and extracts relevant information
        from a TempEval-3 annotated file. Those information are packed in a
        Document object, which is our internal representation.
        """
        assert os.path.isfile(file_path), 'File path does not exist!'
        logging.info('Document {}: parsing...'.format(
            os.path.relpath(file_path)))
        xml = etree.parse(file_path)
        text_node = xml.findall(".//TEXT")[0]
        text_string = etree.tostring(text_node, method='text', encoding='utf8')
        text_xml = etree.tostring(text_node, method='xml', encoding='utf8')
        text_string = unicode(text_string, 'UTF-8')
        text_xml = unicode(text_xml, 'UTF-8')
        right_chars = len(text_xml.split('</TEXT>')[1])
        text_string = text_string[:-right_chars]
        text_xml = etree.tostring(text_node)

        # StanfordParser strips internally the text :(
        left_chars = len(text_string) - len(text_string.lstrip())
        with Mute_stderr():
            stanford_tree = CORENLP.parse(text_string)

        document = Document(file_path)
        document.text_offset = left_chars
        document.file_path = os.path.abspath(file_path)
        document.doc_id = os.path.basename(file_path)
        document.sec_times = self.get_dct(file_path)
        document.dct = document.sec_times.admission_date
        document.dct_text = document.dct.replace('-', '')
        document.title = os.path.basename(file_path)
        document.text = text_string
        document._coref = stanford_tree.get('coref', [])

        for num_sen, stanford_sentence in\
                enumerate(stanford_tree['sentences']):
            collp_deps = stanford_sentence.get('collapsed_dependencies', None)
            basic_deps = stanford_sentence.get('basic_dependencies', None)
            parsetree = stanford_sentence.get('parsetree', u'')

            sentence_text = stanford_sentence.get('text', u'')

            sentence = Sentence(id_sentence=num_sen,
                                basic_dependencies=basic_deps,
                                collapsed_dependencies=collp_deps,
                                parsetree=parsetree,
                                text=sentence_text)
            for num_word, (word_form, attr) in\
                    enumerate(stanford_sentence['words']):
                offset_begin = int(attr['CharacterOffsetBegin']) - left_chars
                offset_end = int(attr['CharacterOffsetEnd']) - left_chars
                word = Word(word_form=word_form,
                            char_offset_begin=offset_begin,
                            char_offset_end=offset_end,
                            lemma=attr['Lemma'],
                            named_entity_tag=attr['NamedEntityTag'],
                            part_of_speech=attr['PartOfSpeech'],
                            id_token=num_word,
                            id_sentence=num_sen)
                sentence.words.append(word)
            document.sentences.append(sentence)

        document.gold_annotations = self._get_annotations(xml, document)
        document.store_gold_annotations()
        document.complete_structure()

        logging.info('Document {}: parsed.'.format(os.path.relpath(file_path)))
        return document
 def edit(self):
     self.categories = Category.all().order("-post_at")
     self.rec = Document.get(self.params.get("id"))
     self.pages = db.GqlQuery("SELECT * FROM Page WHERE document = :1 ORDER BY order", db.Key(self.params.get("id")))
class DocumentController():
    """
    The document controller. Hooks high level (read file/write file, add
    submodel, etc) UI elements to their corresponding actions in the model
    """

    def __init__(self, firstuse):         
        app().documentControllers.add(self)
        self.doc = Document()
        self.idbank = IdBank()
        self.undoStack = QUndoStack()
        app().undoGroup.addStack(self.undoStack)
        self.win = DocumentWindow(docCtrlr=self)
        self.win.show()
        self.stdwin = StdWindow()
        self.webwin = WebWindow()
        self._filename = "untitled.cadnano"
        self.treeController = TreeController(self.win.treeview)
        self.createConnections()
        
        self.recentButton = None
        
        self.win.sliceGraphicsView.mouseMoveEvent = self.sliceViewMouseMove

        if firstuse == True:
            self.win.helplabel.setText(QString("It appears as though this is your first time using CaDNAno 2, would you like to view a tutorial?"))
        else:
            self.win.ignoreButton.hide()
            self.win.showHelpButton.hide()
            self.win.helplabel.hide()

    def sliceViewMouseMove(self, event):
        
        print "hello"
    """
    Roger Additions
    """
    """
    def helpwindow(self):
        #file = open("usagetracker.txt", 'w')
        filename = "C:/Users/Roger Conturie/workspace/Final Cadnano/cadnano2/ui/usagetracker.txt"
        file = open(filename, 'r')
        timesopened = file.readline()
        file.close()
        if timesopened == '0':
            self.webwin.show()
        file = open(filename, 'w')
        file.write(str(int(timesopened) + 1))
        file.close()
    """

    def standardHelpClicked(self):
        self.stdwin.show()
    
    def networkHelpClicked(self):
        import os
        if os.system("ping google.com -n 1") == 1:
            print "no connection"
        else:
            self.webwin.show()
    
    def ignoreClicked(self):
        self.win.ignoreButton.hide()
        self.win.showHelpButton.hide()
        self.win.helplabel.hide()
        
    def showHelpClicked(self):
        self.stdwin.show()
    """
    End Roger Additions
    """
    
    def filename(self):
        return self._filename

    def setFilename(self, proposedFName):
        if self._filename == proposedFName:
            return True
        self._filename = proposedFName
        self.setDirty(True)
        return True

    def createConnections(self):
        """
        Organizational method to collect signal/slot connectors.
        """
        self.win.actionNewHoneycombPart.triggered.connect(self.hcombClicked)

        self.win.actionNewSquarePart.triggered.connect(self.squareClicked)
        self.win.actionNew.triggered.connect(app().newDocument)
        self.win.actionOpen.triggered.connect(self.openClicked)
        self.win.actionClose.triggered.connect(self.closeClicked)
        self.win.actionSave.triggered.connect(self.saveClicked)
        self.win.actionSVG.triggered.connect(self.svgClicked)
        
        self.win.actionStandard_Help.triggered.connect(self.standardHelpClicked)
        self.win.actionNetwork_Help.triggered.connect(self.networkHelpClicked)
        self.win.ignoreButton.pressed.connect(self.ignoreClicked)
        self.win.showHelpButton.pressed.connect(self.showHelpClicked)
        # self.win.actionSave_As.triggered.connect(self.saveAsClicked)
        # self.win.actionQuit.triggered.connect(self.closeClicked)
        # self.win.actionAdd.triggered.connect(self.addClicked)
        # self.win.actionDelete.triggered.connect(self.deleteClicked)
        # self.win.actionCut.triggered.connect(self.cutClicked)
        # self.win.actionPaste.triggered.connect(self.pasteClicked)
        # self.win.actionMoveUp.triggered.connect(self.moveUpClicked)
        # self.win.actionMoveDown.triggered.connect(self.moveDownClicked)
        # self.win.actionPromote.triggered.connect(self.promoteClicked)
        # self.win.actionDemote.triggered.connect(self.demoteClicked)
    # end def

        
    def setDirty(self, dirty=True):
        self.win.setWindowModified(dirty)
    #end def

    def newClicked(self):
        """docstring for newClicked"""
        print "new clicked"
        self.recentButton = "new"
    # end def
    def openClicked(self):
        """docstring for openClicked"""
        print "open clicked"
        self.recentButton = "open"
    # end def        
    def closeClicked(self):
        """docstring for closeClicked"""
        print "close clicked"
        self.recentButton = "close"
    # end def

    def saveClicked(self):
        """docstring for saveClicked"""
        try:
            f = open(self.filename())
            f.write(encode(self.doc))
            f.close()
        except Exception:
            print "Save "
        return True
        self.recentButton = "save"
        
    def saveAsClicked():
        filename = self.filename()
        if filename == None:
            directory = "."
        else:
            directory = QFileInfo(filename).path()
        filename = QFileDialog.getSaveFileName(self.win,\
                            "%s - Save As" % QApplication.applicationName(),\
                            directory,\
                            "%s (*.cadnano)" % QApplication.applicationName())
        if filename.isEmpty():
            return False
        if not filename.toLower().endswith(".cadnano"):
            filename += ".cadnano"
        self.setFilename(filename)
        self.recentButton = "saveAs"
        return self.saveClicked()

    def svgClicked(self):
        """docstring for svgClicked"""
        print "svg clicked"
        self.recentButton = "svg"

    # end def

    def hcombClicked(self):
        """docstring for hcombClicked"""
        print "+honeycomb clicked"
        self.addHoneycombHelixGroup()
        self.recentButton = "hcomb"

    # end def

    def squareClicked(self):
        """docstring for squareClicked"""
        print "+square clicked"
        self.recentButton = "square"
    # end def

    def addHoneycombHelixGroup(self, nrows=20, ncolumns=20):
        """docstring for addHoneycombHelixGroup"""
        # Create a new DNA part
        objId = self.idbank.get()
        instId = objId#self.idbank.get()
        dnaPartInst = self.doc.addDnaPart(objId, instId,\
                                          crossSectionType='honeycomb')
        # Add the part to the Tree view
        name = "Part.%d" % objId 
        self.treeController.addPartNode(name, dnaPartInst)

        # Create a Slice view of part
        shg = SliceHelixGroup(dnaPartInst, nrows, ncolumns,\
                              scene=self.win.slicescene,\
                              controller=self.win.sliceController)
        self.win.slicescene.addItem(shg)

        # Create a Path view of the part
        phg = PathHelixGroup(dnaPartInst, scene=self.win.pathscene,\
                             controller=self.win.pathController)
        self.win.pathscene.addItem(phg)

        # Connect the slice
        shg.helixAdded.connect(phg.handleHelixAdded)
        shg.sliceHelixClicked.connect(phg.handleSliceHelixClick)
        dnaPartInst.partselected.connect(shg.bringToFront)
        dnaPartInst.partselected.connect(phg.bringToFront)
        self.recentButton = "addHoneycombHelixGroup"

    # end def

    def deleteClicked(self):
        index = self.win.treeview.currentIndex()
        if not index.isValid():
            return
        name = self.treemodel.data(index).toString()
        rows = self.treemodel.rowCount(index)
        if rows == 0:
            message = "<p>Delete '%s'" % name
        # end if
        elif rows == 1:
            message = "<p>Delete '%s' and its child (and " +\
                         "grandchildren etc.)" % name
        # end elif
        elif rows > 1:
            message = "<p>Delete '%s' and its %d children (and " +\
                         "grandchildren etc.)" % (name, rows)

        # end elif
        if not self.okToDelete(this, QString("Delete"), QString(message)):
            return
        self.treemodel.removeRow(index.row(), index.parent())
        self.setDirty(True)
        self.updateUi()
        self.recentButton = "addHoneycombHelixGroup"

    # end def

    def okToDelete(self, parent, title, text, detailedText):
        """
        """
        messageBox = QMessageBox(parent)
        if parent:
            messageBox.setWindowModality(Qt.WindowModal)
        # end if
        messageBox.setIcon(QMessageBox.Question)
        messageBox.setWindowTitle(\
            QString("%1 - %2").arg(QApplication.applicationName()).arg(title))
        messageBox.setText(text)
        if not detailedText.isEmpty():
            messageBox.setInformativeText(detailedText)
        # end if
        deleteButton = messageBox.addButton(QString("&Delete"),\
                                            QMessageBox.AcceptRole)
        messageBox.addButton(QString("Do &Not Delete"),\
                             QMessageBox.RejectRole)
        messageBox.setDefaultButton(deleteButton)
        messageBox.exec_()
        return messageBox.clickedButton() == deleteButton
    # end def

    def okToClear(self, savedata, parent, title, text, detailedText):
        """
        savedata is a function pointer
        """
        assert savedata and parent
        messageBox = QMessageBox(parent)
        if parent:
            messageBox.setWindowModality(Qt.WindowModal)
        # end if
        messageBox.setIcon(QMessageBox.Question)
        messageBox.setWindowTitle(\
          QString("%1 - %2").arg(QApplication.applicationName()).arg(title))
        messageBox.setText(text)
        if not detailedText.isEmpty():
            messageBox.setInformativeText(detailedText)
        # end if

        saveButton = messageBox.addButton(QMessageBox.Save)
        messageBox.addButton(QMessageBox.Save)
        messageBox.addButton(QMessageBox.Discard)
        messageBox.addButton(QMessageBox.Cancel)
        messageBox.setDefaultButton(saveButton)
        messageBox.exec_()
        if messageBox.clickedButton() == messageBox.button(QMessageBox.Cancel):
            return False
        if messageBox.clickedButton() == messageBox.button(QMessageBox.Save):
            return parent.savedata()  # how to return the function (lambda?)
        return True
    # end def

    def createAction(self, icon, text, parent, shortcutkey):
        """
        returns a QAction object
        """
        action = QAction(QIcon(icon), text, parent)
        if not shorcutkey.isEmpty():
            action.setShortcut(shortcutkey)
        # end if
        return action
    # end def

    def updateUi(self):
        """
        """
        #self.win.actionSave.setEnabled(self.win.isWindowModified())

        rows = self.treemodel.rowCount()

        #self.win.actionSave_As.setEnabled(self.win.isWindowModified() or rows)
        #self.win.actionHideOrShowItems.setEnabled(rows)
        enable = self.win.treeview.currentIndex().isValid()

        # actions = [self.win.actionDelete,\
        #            self.win.actionMoveUp,\
        #            self.win.actionMoveDown,\
        #            self.win.actionCut,\
        #            self.win.actionPromote,\
        #            self.win.actionDemote]
        # for action in actions:
        #     action.setEnabled(enable)
        # # end for
        # self.win.actionStartOrStop.setEnabled(rows);
        # self.win.actionPaste.setEnabled(self.treemodel.hasCutItem())
    #end def

    def cutClicked(self):
        """"""
        self.win.actionPaste.setEnabled(self.treeController.cut())
    # end def

    def pasteClicked(self):
        """"""
        self.treeController.paste()
    # end def

    def moveUpClicked(self):
        """"""
        self.treeController.moveUp()
    # end def

    def moveDownClicked(self):
        """"""
        self.treeController.moveDown()
    # end def

    def promoteClicked(self):
        """"""
        self.treeController.promote()
    #end def

    def demoteClicked(self):
        """"""
        self.treeController.demote()
    #end def

    def hideOrShowNode(self, hide, index):
        """"""
        self.treeController.hideOrShowNode()
Exemple #33
0
 def __init__(self, term="", posting=Posting(Document())):
     self.posting = posting
     self.term = term
Exemple #34
0
 def init(cls):
     cls._current_document = Document()
     log = vtk.vtkFileOutputWindow()  #para guardar los mensajes de vtk
     log.SetFileName("logs/vtk.log")  #fichero donde se guardan
     log.SetInstance(log)  #usar este fichero
class DocumentController():
    """
    Connects UI buttons to their corresponding actions in the model.
    """
    ### INIT METHODS ###
    def __init__(self):
        """docstring for __init__"""
        # initialize variables
        self._document = Document()
        self._document.setController(self)
        self._activePart = None
        self._filename = None
        self._fileOpenPath = None  # will be set in _readSettings
        self._hasNoAssociatedFile = True
        self._pathViewInstance = None
        self._sliceViewInstance = None
        self._undoStack = None
        self.win = None
        self.fileopendialog = None
        self.filesavedialog = None

        self.settings = QSettings()
        self._readSettings()

        # call other init methods
        self._initWindow()
        if app().isInMaya():
            self._initMaya()
        app().documentControllers.add(self)

    def _initWindow(self):
        """docstring for initWindow"""
        self.win = DocumentWindow(docCtrlr=self)
        self.win.setWindowIcon(QIcon('ui/mainwindow/images/cadnano2-app-icon.png'))
        app().documentWindowWasCreatedSignal.emit(self._document, self.win)
        self._connectWindowSignalsToSelf()
        self.win.show()

    def _initMaya(self):
        """
        Initialize Maya-related state. Delete Maya nodes if there
        is an old document left over from the same session. Set up
        the Maya window.
        """
        # There will only be one document
        if (app().activeDocument and app().activeDocument.win and
                                not app().activeDocument.win.close()):
            return
        del app().activeDocument
        app().activeDocument = self

        import maya.OpenMayaUI as OpenMayaUI
        import sip
        ptr = OpenMayaUI.MQtUtil.mainWindow()
        mayaWin = sip.wrapinstance(long(ptr), QMainWindow)
        self.windock = QDockWidget("cadnano")
        self.windock.setFeatures(QDockWidget.DockWidgetMovable
                                 | QDockWidget.DockWidgetFloatable)
        self.windock.setAllowedAreas(Qt.LeftDockWidgetArea
                                     | Qt.RightDockWidgetArea)
        self.windock.setWidget(self.win)
        mayaWin.addDockWidget(Qt.DockWidgetArea(Qt.LeftDockWidgetArea),
                                self.windock)
        self.windock.setVisible(True)

    def _connectWindowSignalsToSelf(self):
        """This method serves to group all the signal & slot connections
        made by DocumentController"""
        self.win.actionNew.triggered.connect(self.actionNewSlot)
        self.win.actionOpen.triggered.connect(self.actionOpenSlot)
        self.win.actionClose.triggered.connect(self.actionCloseSlot)
        self.win.actionSave.triggered.connect(self.actionSaveSlot)
        self.win.actionSave_As.triggered.connect(self.actionSaveAsSlot)
        self.win.actionSVG.triggered.connect(self.actionSVGSlot)
        self.win.actionAutoStaple.triggered.connect(self.actionAutostapleSlot)
        self.win.actionExportStaples.triggered.connect(self.actionExportStaplesSlot)
        self.win.actionPreferences.triggered.connect(self.actionPrefsSlot)
        self.win.actionModify.triggered.connect(self.actionModifySlot)
        self.win.actionNewHoneycombPart.triggered.connect(\
            self.actionAddHoneycombPartSlot)
        self.win.actionNewSquarePart.triggered.connect(\
            self.actionAddSquarePartSlot)
        self.win.closeEvent = self.windowCloseEventHandler
        self.win.actionAbout.triggered.connect(self.actionAboutSlot)
        self.win.actionCadnanoWebsite.triggered.connect(self.actionCadnanoWebsiteSlot)
        self.win.actionFeedback.triggered.connect(self.actionFeedbackSlot)
        self.win.actionFilterHandle.triggered.connect(self.actionFilterHandleSlot)
        self.win.actionFilterEndpoint.triggered.connect(self.actionFilterEndpointSlot)
        self.win.actionFilterStrand.triggered.connect(self.actionFilterStrandSlot)
        self.win.actionFilterXover.triggered.connect(self.actionFilterXoverSlot)
        self.win.actionFilterScaf.triggered.connect(self.actionFilterScafSlot)
        self.win.actionFilterStap.triggered.connect(self.actionFilterStapSlot)
        self.win.actionRenumber.triggered.connect(self.actionRenumberSlot)


    ### SLOTS ###
    def undoStackCleanChangedSlot(self):
        """The title changes to include [*] on modification."""
        self.win.setWindowModified(not self.undoStack().isClean())
        self.win.setWindowTitle(self.documentTitle())

    def actionAboutSlot(self):
        """Displays the about cadnano dialog."""
        from ui.dialogs.ui_about import Ui_About
        dialog = QDialog()
        dialogAbout = Ui_About()  # reusing this dialog, should rename
        dialog.setStyleSheet("QDialog { background-image: url(ui/dialogs/images/cadnano2-about.png); background-repeat: none; }")
        dialogAbout.setupUi(dialog)
        dialog.exec_()

    filterList = ["strand", "endpoint", "xover", "virtualHelix"]
    def actionFilterHandleSlot(self):
        """Disables all other selection filters when active."""
        fH = self.win.actionFilterHandle
        fE = self.win.actionFilterEndpoint
        fS = self.win.actionFilterStrand
        fX = self.win.actionFilterXover
        fH.setChecked(True)
        if fE.isChecked():
            fE.setChecked(False)
        if fS.isChecked():
            fS.setChecked(False)
        if fX.isChecked():
            fX.setChecked(False)
        self._document.documentSelectionFilterChangedSignal.emit(["virtualHelix"])

    def actionFilterEndpointSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.actionFilterHandle
        fE = self.win.actionFilterEndpoint
        fS = self.win.actionFilterStrand
        fX = self.win.actionFilterXover
        if fH.isChecked():
            fH.setChecked(False)
        if not fS.isChecked() and not fX.isChecked():
            fE.setChecked(True)
        self._strandFilterUpdate()
    # end def

    def actionFilterStrandSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.actionFilterHandle
        fE = self.win.actionFilterEndpoint
        fS = self.win.actionFilterStrand
        fX = self.win.actionFilterXover
        if fH.isChecked():
            fH.setChecked(False)
        if not fE.isChecked() and not fX.isChecked():
            fS.setChecked(True)
        self._strandFilterUpdate()
    # end def

    def actionFilterXoverSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.actionFilterHandle
        fE = self.win.actionFilterEndpoint
        fS = self.win.actionFilterStrand
        fX = self.win.actionFilterXover
        if fH.isChecked():
            fH.setChecked(False)
        if not fE.isChecked() and not fS.isChecked():
            fX.setChecked(True)
        self._strandFilterUpdate()
    # end def

    def actionFilterScafSlot(self):
        """Remains checked if no other strand-type filter is active."""
        fSc = self.win.actionFilterScaf
        fSt = self.win.actionFilterStap
        if not fSc.isChecked() and not fSt.isChecked():
            fSc.setChecked(True)
        self._strandFilterUpdate()

    def actionFilterStapSlot(self):
        """Remains checked if no other strand-type filter is active."""
        fSc = self.win.actionFilterScaf
        fSt = self.win.actionFilterStap
        if not fSc.isChecked() and not fSt.isChecked():
            fSt.setChecked(True)
        self._strandFilterUpdate()
    # end def

    def _strandFilterUpdate(self):
        win = self.win
        filterList = []
        if win.actionFilterEndpoint.isChecked():
            filterList.append("endpoint")
        if win.actionFilterStrand.isChecked():
            filterList.append("strand")
        if win.actionFilterXover.isChecked():
            filterList.append("xover")
        if win.actionFilterScaf.isChecked():
            filterList.append("scaffold")
        if win.actionFilterStap.isChecked():
            filterList.append("staple")
        self._document.documentSelectionFilterChangedSignal.emit(filterList)
    # end def

    def actionNewSlot(self):
        """
        1. If document is has no parts, do nothing.
        2. If document is dirty, call maybeSave and continue if it succeeds.
        3. Create a new document and swap it into the existing ctrlr/window.
        """
        # clear/reset the view!

        if len(self._document.parts()) == 0:
            return  # no parts
        if self.maybeSave() == False:
            return  # user canceled in maybe save
        else:  # user did not cancel
            if self.filesavedialog != None:
                self.filesavedialog.finished.connect(self.newClickedCallback)
            else:  # user did not save
                self.newClickedCallback()  # finalize new

    def actionOpenSlot(self):
        """
        1. If document is untouched, proceed to open dialog.
        2. If document is dirty, call maybesave and continue if it succeeds.
        Downstream, the file is selected in openAfterMaybeSave,
        and the selected file is actually opened in openAfterMaybeSaveCallback.
        """
        if self.maybeSave() == False:
            return  # user canceled in maybe save
        else:  # user did not cancel
            if hasattr(self, "filesavedialog"): # user did save
                if self.filesavedialog != None:
                    self.filesavedialog.finished.connect(self.openAfterMaybeSave)
                else:
                    self.openAfterMaybeSave()  # windows
            else:  # user did not save
                self.openAfterMaybeSave()  # finalize new

    def actionCloseSlot(self):
        """This will trigger a Window closeEvent."""
        #print "closing"
        if util.isWindows():
            #print "close win"
            self.win.close()
            if not app().isInMaya():
                #print "exit app"
                import sys
                sys.exit(1)

    def actionSaveSlot(self):
        """SaveAs if necessary, otherwise overwrite existing file."""
        if self._hasNoAssociatedFile:
            self.saveFileDialog()
            return
        self.writeDocumentToFile()

    def actionSaveAsSlot(self):
        """Open a save file dialog so user can choose a name."""
        self.saveFileDialog()

    def actionSVGSlot(self):
        """docstring for actionSVGSlot"""
        fname = os.path.basename(str(self.filename()))
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()

        fdialog = QFileDialog(
                    self.win,
                    "%s - Save As" % QApplication.applicationName(),
                    directory,
                    "%s (*.svg)" % QApplication.applicationName())
        fdialog.setAcceptMode(QFileDialog.AcceptSave)
        fdialog.setWindowFlags(Qt.Sheet)
        fdialog.setWindowModality(Qt.WindowModal)
        self.svgsavedialog = fdialog
        self.svgsavedialog.filesSelected.connect(self.saveSVGDialogCallback)
        fdialog.open()

    class DummyChild(QGraphicsItem):
        def boundingRect(self):
            return QRect(200, 200) # self.parentObject().boundingRect()
        def paint(self, painter, option, widget=None):
            pass

    def saveSVGDialogCallback(self, selected):
        if isinstance(selected, QStringList) or isinstance(selected, list):
            fname = selected[0]
        else:
            fname = selected
        if fname.isEmpty() or os.path.isdir(fname):
            return False
        fname = str(fname)
        if not fname.lower().endswith(".svg"):
            fname += ".svg"
        if self.svgsavedialog != None:
            self.svgsavedialog.filesSelected.disconnect(self.saveSVGDialogCallback)
            del self.svgsavedialog  # prevents hang
            self.svgsavedialog = None

        generator = QSvgGenerator()
        generator.setFileName(fname)
        generator.setSize(QSize(200, 200))
        generator.setViewBox(QRect(0, 0, 2000, 2000))
        painter = QPainter()

        # Render through scene
        # painter.begin(generator)
        # self.win.pathscene.render(painter)
        # painter.end()

        # Render item-by-item
        painter = QPainter()
        styleOption = QStyleOptionGraphicsItem()
        q = [self.win.pathroot]
        painter.begin(generator)
        while q:
            graphicsItem = q.pop()
            transform = graphicsItem.itemTransform(self.win.sliceroot)[0]
            painter.setTransform(transform)
            if graphicsItem.isVisible():
                graphicsItem.paint(painter, styleOption, None)
                q.extend(graphicsItem.childItems())
        painter.end()

    def actionExportStaplesSlot(self):
        """
        Triggered by clicking Export Staples button. Opens a file dialog to
        determine where the staples should be saved. The callback is
        exportStaplesCallback which collects the staple sequences and exports
        the file.
        """
        # Validate that no staple oligos are loops.
        part = self.activePart()
        stapLoopOlgs = part.getStapleLoopOligos()
        if stapLoopOlgs:
            from ui.dialogs.ui_warning import Ui_Warning
            dialog = QDialog()
            dialogWarning = Ui_Warning()  # reusing this dialog, should rename
            dialog.setStyleSheet("QDialog { background-image: url(ui/dialogs/images/cadnano2-about.png); background-repeat: none; }")
            dialogWarning.setupUi(dialog)

            locs = ", ".join([o.locString() for o in stapLoopOlgs])
            msg = "Part contains staple loop(s) at %s.\n\nUse the break tool to introduce 5' & 3' ends before exporting. Loops have been colored red; use undo to revert." % locs
            dialogWarning.title.setText("Staple validation failed")
            dialogWarning.message.setText(msg)
            for o in stapLoopOlgs:
                o.applyColor(styles.stapColors[0].name())
            dialog.exec_()
            return

        # Proceed with staple export.
        fname = self.filename()
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getSaveFileName(
                            self.win,
                            "%s - Export As" % QApplication.applicationName(),
                            directory,
                            "(*.csv)")
            self.saveStaplesDialog = None
            self.exportStaplesCallback(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                            self.win,
                            "%s - Export As" % QApplication.applicationName(),
                            directory,
                            "(*.csv)")
            fdialog.setAcceptMode(QFileDialog.AcceptSave)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.saveStaplesDialog = fdialog
            self.saveStaplesDialog.filesSelected.connect(self.exportStaplesCallback)
            fdialog.open()
    # end def

    def actionPrefsSlot(self):
        app().prefsClicked()

    def actionAutostapleSlot(self):
        part = self.activePart()
        if part:
            self.win.pathGraphicsView.setViewportUpdateOn(False)
            part.autoStaple()
            self.win.pathGraphicsView.setViewportUpdateOn(True)

    def actionModifySlot(self):
        """
        Notifies that part root items that parts should respond to modifier
        selection signals.
        """
        # uncomment for debugging
        # isChecked = self.win.actionModify.isChecked()
        # self.win.pathroot.setModifyState(isChecked)
        # self.win.sliceroot.setModifyState(isChecked)
        if app().isInMaya():
            isChecked = self.win.actionModify.isChecked()
            self.win.pathroot.setModifyState(isChecked)
            self.win.sliceroot.setModifyState(isChecked)
            self.win.solidroot.setModifyState(isChecked)

    def actionAddHoneycombPartSlot(self):
        """docstring for actionAddHoneycombPartSlot"""
        part = self._document.addHoneycombPart()
        self.setActivePart(part)

    def actionAddSquarePartSlot(self):
        """docstring for actionAddSquarePartSlot"""
        part = self._document.addSquarePart()
        self.setActivePart(part)

    def actionRenumberSlot(self):
        coordList = self.win.pathroot.getSelectedPartOrderedVHList()
        part = self.activePart()
        part.renumber(coordList)
    # end def

    ### ACCESSORS ###
    def document(self):
        return self._document

    def window(self):
        return self.win

    def setDocument(self, doc):
        """
        Sets the controller's document, and informs the document that
        this is its controller.
        """
        self._document = doc
        doc.setController(self)

    def activePart(self):
        if self._activePart == None:
            self._activePart = self._document.selectedPart()
        return self._activePart

    def setActivePart(self, part):
        self._activePart = part

    def undoStack(self):
        return self._document.undoStack()

    ### PRIVATE SUPPORT METHODS ###
    def newDocument(self, doc=None, fname=None):
        """Creates a new Document, reusing the DocumentController."""
        self._document.resetViews()
        self._document.removeAllParts()  # clear out old parts
        self._document.undoStack().clear()  # reset undostack
        self._filename = fname if fname else "untitled.json"
        self._hasNoAssociatedFile = fname == None
        self._activePart = None
        self.win.setWindowTitle(self.documentTitle() + '[*]')

    def saveFileDialog(self):
        fname = self.filename()
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getSaveFileName(
                            self.win,
                            "%s - Save As" % QApplication.applicationName(),
                            directory,
                            "%s (*.json)" % QApplication.applicationName())
            self.writeDocumentToFile(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                            self.win,
                            "%s - Save As" % QApplication.applicationName(),
                            directory,
                            "%s (*.json)" % QApplication.applicationName())
            fdialog.setAcceptMode(QFileDialog.AcceptSave)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.filesavedialog = fdialog
            self.filesavedialog.filesSelected.connect(
                                                self.saveFileDialogCallback)
            fdialog.open()
    # end def

    def _readSettings(self):
        """
        Reads settings.
        Currenly this just loads the path of the last opened file, which is stored as self._fileOpenPath
        """
        self.settings.beginGroup("FileSystem")
        self._fileOpenPath = self.settings.value("openpath", QDir().homePath()).toString()
        self.settings.endGroup()

    def _writeFileOpenPath(self, path):
        """
        Makes sure to save/remember the *directory* path of the last opened file to settings.
        """
        self._fileOpenPath = path
        self.settings.beginGroup("FileSystem")
        self.settings.setValue("openpath", path)
        self.settings.endGroup()

    ### SLOT CALLBACKS ###
    def actionNewSlotCallback(self):
        """
        Gets called on completion of filesavedialog after newClicked's
        maybeSave. Removes the dialog if necessary, but it was probably
        already removed by saveFileDialogCallback.
        """
        if self.filesavedialog != None:
            self.filesavedialog.finished.disconnect(self.actionNewSlotCallback)
            del self.filesavedialog  # prevents hang (?)
            self.filesavedialog = None
        self.newDocument()

    def exportStaplesCallback(self, selected):
        """Export all staple sequences to selected CSV file."""
        if isinstance(selected, QStringList) or isinstance(selected, list):
            fname = selected[0]
        else:
            fname = selected
        if fname.isEmpty() or os.path.isdir(fname):
            return False
        fname = str(fname)
        if not fname.lower().endswith(".csv"):
            fname += ".csv"
        if self.saveStaplesDialog != None:
            self.saveStaplesDialog.filesSelected.disconnect(self.exportStaplesCallback)
            # manual garbage collection to prevent hang (in osx)
            del self.saveStaplesDialog
            self.saveStaplesDialog = None
        # write the file
        output = self.activePart().getStapleSequences()
        with open(fname, 'w') as f:
            f.write(output)
    # end def

    def newClickedCallback(self):
        """
        Gets called on completion of filesavedialog after newClicked's
        maybeSave. Removes the dialog if necessary, but it was probably
        already removed by saveFileDialogCallback.
        """

        if self.filesavedialog != None:
            self.filesavedialog.finished.disconnect(self.newClickedCallback)
            del self.filesavedialog  # prevents hang (?)
            self.filesavedialog = None
        self.newDocument()

    def openAfterMaybeSaveCallback(self, selected):
        """
        Receives file selection info from the dialog created by
        openAfterMaybeSave, following user input.

        Extracts the file name and passes it to the decode method, which
        returns a new document doc, which is then set as the open document
        by newDocument. Calls finalizeImport and disconnects dialog signaling.
        """
        if isinstance(selected, QStringList) or isinstance(selected, list):
            fname = selected[0]
        else:
            fname = selected
        if not fname or os.path.isdir(fname):
            return False
        fname = str(fname)
        self._writeFileOpenPath(os.path.dirname(fname))
        self.newDocument(fname=fname)
        with open(fname) as fd:
            decode(self._document, fd.read())
        if hasattr(self, "filesavedialog"): # user did save
            if self.fileopendialog != None:
                self.fileopendialog.filesSelected.disconnect(\
                                              self.openAfterMaybeSaveCallback)
            # manual garbage collection to prevent hang (in osx)
            del self.fileopendialog
            self.fileopendialog = None

    def saveFileDialogCallback(self, selected):
        """If the user chose to save, write to that file."""
        if isinstance(selected, QStringList) or isinstance(selected, list):
            fname = selected[0]
        else:
            fname = selected
        if fname.isEmpty() or os.path.isdir(fname):
            return False
        fname = str(fname)
        if not fname.lower().endswith(".json"):
            fname += ".json"
        if self.filesavedialog != None:
            self.filesavedialog.filesSelected.disconnect(
                                                self.saveFileDialogCallback)
            del self.filesavedialog  # prevents hang
            self.filesavedialog = None
        self.writeDocumentToFile(fname)
        self._writeFileOpenPath(os.path.dirname(fname))

    ### EVENT HANDLERS ###
    def windowCloseEventHandler(self, event):
        """Intercept close events when user attempts to close the window."""
        if self.maybeSave():
            event.accept()
            if app().isInMaya():
                self.windock.setVisible(False)
                del self.windock
                self.windock = None
            app().documentControllers.remove(self)
        else:
            event.ignore()
        self.actionCloseSlot()

    ### FILE INPUT ##
    def documentTitle(self):
        fname = os.path.basename(str(self.filename()))
        if not self.undoStack().isClean():
            fname += '[*]'
        return fname

    def filename(self):
        return self._filename

    def setFilename(self, proposedFName):
        if self._filename == proposedFName:
            return True
        self._filename = proposedFName
        self._hasNoAssociatedFile = False
        self.win.setWindowTitle(self.documentTitle())
        return True

    def openAfterMaybeSave(self):
        """
        This is the method that initiates file opening. It is called by
        actionOpenSlot to spawn a QFileDialog and connect it to a callback
        method.
        """
        path = self._fileOpenPath
        if util.isWindows():  # required for native looking file window#"/",
            fname = QFileDialog.getOpenFileName(
                        None,
                        "Open Document", path,
                        "cadnano1 / cadnano2 Files (*.nno *.json *.cadnano)")
            self.filesavedialog = None
            self.openAfterMaybeSaveCallback(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                        self.win,
                        "Open Document",
                        path,
                        "cadnano1 / cadnano2 Files (*.nno *.json *.cadnano)")
            fdialog.setAcceptMode(QFileDialog.AcceptOpen)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.fileopendialog = fdialog
            self.fileopendialog.filesSelected.connect(self.openAfterMaybeSaveCallback)
            fdialog.open()
    # end def

    ### FILE OUTPUT ###
    def maybeSave(self):
        """Save on quit, check if document changes have occured."""
        if app().dontAskAndJustDiscardUnsavedChanges:
            return True
        if not self.undoStack().isClean():    # document dirty?
            savebox = QMessageBox(QMessageBox.Warning,   "Application",
                "The document has been modified.\nDo you want to save your changes?",
                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel,
                self.win,
                Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint | Qt.Sheet)
            savebox.setWindowModality(Qt.WindowModal)
            save = savebox.button(QMessageBox.Save)
            discard = savebox.button(QMessageBox.Discard)
            cancel = savebox.button(QMessageBox.Cancel)
            save.setShortcut("Ctrl+S")
            discard.setShortcut(QKeySequence("D,Ctrl+D"))
            cancel.setShortcut(QKeySequence("C,Ctrl+C,.,Ctrl+."))
            ret = savebox.exec_()
            del savebox  # manual garbage collection to prevent hang (in osx)
            if ret == QMessageBox.Save:
                return self.actionSaveAsSlot()
            elif ret == QMessageBox.Cancel:
                return False
        return True

    def writeDocumentToFile(self, filename=None):
        if filename == None:
            assert(not self._hasNoAssociatedFile)
            filename = self.filename()
        try:
            with open(filename, 'w') as f:
                helixOrderList = self.win.pathroot.getSelectedPartOrderedVHList()
                encode(self._document, helixOrderList, f)
        except IOError:
            flags = Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint | Qt.Sheet
            errorbox = QMessageBox(QMessageBox.Critical,
                                   "cadnano",
                                   "Could not write to '%s'." % filename,
                                   QMessageBox.Ok,
                                   self.win,
                                   flags)
            errorbox.setWindowModality(Qt.WindowModal)
            errorbox.open()
            return False
        self.undoStack().setClean()
        self.setFilename(filename)
        return True

    def actionCadnanoWebsiteSlot(self):
        import webbrowser
        webbrowser.open("http://cadnano.org/")

    def actionFeedbackSlot(self):
        import webbrowser
        webbrowser.open("http://cadnano.org/feedback")
Exemple #36
0
def test_setup_document():
    my_document = Document(1, "data")
    assert(my_document.identifier == 1 and my_document.data == "data")
Exemple #37
0
class DocumentController:
    """
    Connects UI buttons to their corresponding actions in the model.
    """

    ### INIT METHODS ###
    def __init__(self, doc=None, fname=None):
        """docstring for __init__"""
        # initialize variables
        self._document = Document() if not doc else doc
        self._filename = fname
        self.win = None
        self._undoStack = None
        self._hasNoAssociatedFile = None
        self._sliceViewInstance = None
        self._pathViewInstance = None
        self._solidView = None
        self._activePart = None
        # call other init methods
        self.initWindow()
        self.initMaya()
        app().documentControllers.add(self)

    def initWindow(self):
        """docstring for initWindow"""
        self.win = DocumentWindow(docCtrlr=self)
        self.connectWindowSignalsToSelf()
        self.win.show()

    def connectWindowSignalsToSelf(self):
        """This method serves to group all the signal & slot connections
        made by DocumentController"""
        self.win.actionNew.triggered.connect(self.actionNewSlot)
        self.win.actionOpen.triggered.connect(self.actionOpenSlot)
        self.win.actionClose.triggered.connect(self.actionCloseSlot)
        self.win.actionSave.triggered.connect(self.actionSaveSlot)
        self.win.actionSave_As.triggered.connect(self.actionSaveAsSlot)
        self.win.actionSVG.triggered.connect(self.actionSVGSlot)
        self.win.actionAutoStaple.triggered.connect(self.actionAutostapleSlot)
        self.win.actionExportStaples.triggered.connect(self.actionExportStaplesSlot)
        self.win.actionPreferences.triggered.connect(self.actionPrefsSlot)
        self.win.actionModify.triggered.connect(self.actionModifySlot)
        self.win.actionNewHoneycombPart.triggered.connect(self.actionAddHoneycombPartSlot)
        self.win.actionNewSquarePart.triggered.connect(self.actionAddSquarePartSlot)
        self.win.closeEvent = self.windowCloseEventHandler

    def initMaya(self):
        """docstring for initMaya"""
        if app().isInMaya():
            # There will only be one document
            if app().activeDocument and app().activeDocument.win and not app().activeDocument.win.close():
                return
            del app().activeDocument
            app().deleteAllMayaNodes()
            app().activeDocument = self

    ### SLOTS ###
    def undoStackCleanChangedSlot(self):
        """The title changes to include [*] on modification."""
        self.win.setWindowModified(not self.undoStack().isClean())
        self.win.setWindowTitle(self.documentTitle())

    def actionNewSlot(self):
        """
        1. If document is has no parts, do nothing.
        2. If document is dirty, call maybeSave and continue if it succeeds.
        3. Create a new document and swap it into the existing ctrlr/window.
        """
        if len(self._document.parts()) == 0:
            # print "no parts!"
            return  # no parts

        if self.maybeSave() == False:
            return  # user canceled in maybe save
        else:  # user did not cancel
            if hasattr(self, "filesavedialog"):  # user did save
                self.filesavedialog.finished.connect(self.newClickedCallback)
            else:  # user did not save
                self.newClickedCallback()  # finalize new

    def actionOpenSlot(self):
        """
        1. If document is untouched, proceed to open dialog.
        2. If document is dirty, call maybesave and continue if it succeeds.
        Downstream, the file is selected in openAfterMaybeSave,
        and the selected file is actually opened in openAfterMaybeSaveCallback.
        """
        if self.maybeSave() == False:
            return  # user canceled in maybe save
        else:  # user did not cancel
            if hasattr(self, "filesavedialog"):  # user did save
                if self.filesavedialog != None:
                    self.filesavedialog.finished.connect(self.openAfterMaybeSave)
                else:
                    self.openAfterMaybeSave()  # windows
            else:  # user did not save
                self.openAfterMaybeSave()  # finalize new

    def actionCloseSlot(self):
        """This will trigger a Window closeEvent."""
        if util.isWindows():
            self.win.close()

    def actionSaveSlot(self):
        """SaveAs if necessary, otherwise overwrite existing file."""
        if self._hasNoAssociatedFile:
            self.saveFileDialog()
            return
        # if importedFromJson:
        #     self.saveFileDialog()
        #     return
        self.writeDocumentToFile()

    def actionSaveAsSlot(self):
        """Open a save file dialog so user can choose a name."""
        self.saveFileDialog()

    def actionSVGSlot(self):
        """docstring for actionSVGSlot"""
        pass

    def actionExportStaplesSlot(self):
        """
        Triggered by clicking Export Staples button. Opens a file dialog to
        determine where the staples should be saved. The callback is
        exportCSVCallback which collects the staple sequences and exports
        the file.
        """
        fname = self.filename()
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getSaveFileName(
                self.win, "%s - Export As" % QApplication.applicationName(), directory, "(*.csv)"
            )
            self.saveCSVdialog = None
            self.exportFile(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(self.win, "%s - Export As" % QApplication.applicationName(), directory, "(*.csv)")
            fdialog.setAcceptMode(QFileDialog.AcceptSave)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            # fdialog.exec_()  # or .show(), or .open()
            self.saveCSVdialog = fdialog
            self.saveCSVdialog.filesSelected.connect(self.exportCSVCallback)
            fdialog.open()

    # end def

    def actionPrefsSlot(self):
        """docstring for actionPrefsSlot"""
        app().prefsClicked

    def actionAutostapleSlot(self):
        """docstring for actionAutostapleSlot"""
        self.activePart().autoStaple()

    def actionModifySlot(self):
        """docstring for actionModifySlot"""
        pass

    def actionAddHoneycombPartSlot(self):
        """docstring for actionAddHoneycombPartSlot"""
        part = self._document.addDnaHoneycombPart()
        self.setActivePart(part)

    def actionAddSquarePartSlot(self):
        """docstring for actionAddSquarePartSlot"""
        part = self._document.addDnaSquarePart()
        self.setActivePart(part)

    ### METHODS ###
    def undoStack(self):
        return self._document.undoStack()

    def newDocument(self, doc=None, fname=None):
        """Creates a new Document, reusing the DocumentController."""
        if app().isInMaya():
            app().deleteAllMayaNodes()
        self._document.removeAllParts()  # clear out old parts
        self._undoStack.clear()  # reset undostack
        del self.sliceGraphicsItem
        del self.pathHelixGroup
        self.sliceGraphicsItem = None
        self.pathHelixGroup = None
        self.solidHelixGroup = None
        self._filename = fname if fname else "untitled.nno"
        self._hasNoAssociatedFile = fname == None
        self._activePart = None
        self.win.setWindowTitle(self.documentTitle() + "[*]")
        if doc != None and doc.parts():
            part = doc.parts()[0]
            self._activePart = part
            self.setDocument(doc)
            part.needsFittingToView.emit()  # must come after setDocument
        else:
            self.setDocument(Document())

    def windowCloseEventHandler(self, event):
        """Intercept close events when user attempts to close the window."""
        if self.maybeSave():
            event.accept()
            if app().isInMaya():
                self.windock.setVisible(False)
                del self.windock
            app().documentControllers.remove(self)
        else:
            event.ignore()

    ### slot callbacks ###
    def actionNewSlotCallback(self):
        """
        Gets called on completion of filesavedialog after newClicked's 
        maybeSave. Removes the dialog if necessary, but it was probably
        already removed by saveFileDialogCallback.
        """
        if hasattr(self, "filesavedialog"):  # user did save
            self.filesavedialog.finished.disconnect(self.actionNewSlotCallback)
            del self.filesavedialog  # prevents hang (?)
        self.newDocument()

    def saveFileDialogCallback(self):
        """docstring for saveFileDialogCallback"""
        if isinstance(selected, QStringList) or isinstance(selected, list):
            fname = selected[0]
        else:
            fname = selected
        if fname.isEmpty() or os.path.isdir(fname):
            return False
        fname = str(fname)
        if not fname.lower().endswith(".nno"):
            fname += ".nno"
        if self.filesavedialog != None:
            self.filesavedialog.filesSelected.disconnect(self.saveFileDialogCallback)
            del self.filesavedialog  # prevents hang
        self.writeDocumentToFile(fname)

    def openAfterMaybeSaveCallback(self):
        """docstring for openAfterMaybeSaveCallback"""
        if isinstance(selected, QStringList) or isinstance(selected, list):
            fname = selected[0]
        else:
            fname = selected
        if not fname or os.path.isdir(fname):
            return False
        fname = str(fname)
        doc = decode(file(fname).read())
        self.newDocument(doc, fname)
        doc.finalizeImport()  # updates staple highlighting
        if self.fileopendialog != None:
            self.fileopendialog.filesSelected.disconnect(self.openAfterMaybeSaveCallback)
            # manual garbage collection to prevent hang (in osx)
            del self.fileopendialog

    ### file input ##
    def documentTitle(self):
        fname = os.path.basename(str(self.filename()))
        if not self.undoStack().isClean():
            fname += "[*]"
        return fname

    def filename(self):
        return self._filename

    def setFilename(self):
        if self._filename == proposedFName:
            return True
        self._filename = proposedFName
        self._hasNoAssociatedFile = False
        self.win.setWindowTitle(self.documentTitle())
        return True

    def openAfterMaybeSave(self):
        """docstring for openAfterMaybeSave"""
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getOpenFileName(
                None, "Open Document", "/", "CADnano1 / CADnano2 Files (*.nno *.json *.cadnano)"
            )
            self.filesavedialog = None
            self.openAfterMaybeSaveCallback(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(self.win, "Open Document", "/", "CADnano1 / CADnano2 Files (*.nno *.json *.cadnano)")
            fdialog.setAcceptMode(QFileDialog.AcceptOpen)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            # fdialog.exec_()  # or .show(), or .open()
            self.fileopendialog = fdialog
            self.fileopendialog.filesSelected.connect(self.openAfterMaybeSaveCallback)
            fdialog.open()  # or .show(), or .open()

    def openAfterMaybeSaveCallback(self):
        if isinstance(selected, QStringList) or isinstance(selected, list):
            fname = selected[0]
        else:
            fname = selected
        if not fname or os.path.isdir(fname):
            return False
        fname = str(fname)
        doc = decode(file(fname).read())
        # DocumentController(doc, fname)
        self.newDocument(doc, fname)
        doc.finalizeImport()  # updates staple highlighting
        if self.fileopendialog != None:
            self.fileopendialog.filesSelected.disconnect(self.openAfterMaybeSaveCallback)
            # manual garbage collection to prevent hang (in osx)
            del self.fileopendialog

    ### file output ###
    def maybeSave(self):
        """Save on quit, check if document changes have occured."""
        if app().dontAskAndJustDiscardUnsavedChanges:
            return True
        if not self.undoStack().isClean():  # document dirty?
            savebox = QMessageBox(
                QMessageBox.Warning,
                "Application",
                "The document has been modified.\nDo you want to save your changes?",
                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel,
                self.win,
                Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint | Qt.Sheet,
            )
            savebox.setWindowModality(Qt.WindowModal)
            save = savebox.button(QMessageBox.Save)
            discard = savebox.button(QMessageBox.Discard)
            cancel = savebox.button(QMessageBox.Cancel)
            save.setShortcut("Ctrl+S")
            discard.setShortcut(QKeySequence("D,Ctrl+D"))
            cancel.setShortcut(QKeySequence("C,Ctrl+C,.,Ctrl+."))
            ret = savebox.exec_()
            del savebox  # manual garbage collection to prevent hang (in osx)
            if ret == QMessageBox.Save:
                return self.saveAsClicked()
            elif ret == QMessageBox.Cancel:
                return False
        return True

    def writeDocumentToFile(self, filename=None):
        if filename == None:
            assert not self._hasNoAssociatedFile
            filename = self.filename()
        try:
            f = open(filename, "w")
            encode(self._document, f)
            f.close()
        except IOError:
            flags = Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint | Qt.Sheet
            errorbox = QMessageBox(
                QMessageBox.Critical, "CaDNAno", "Could not write to '%s'." % filename, QMessageBox.Ok, self.win, flags
            )
            errorbox.setWindowModality(Qt.WindowModal)
            errorbox.open()
            return False
        self.undoStack().setClean()
        self.setFilename(filename)
        return True

    def saveFileDialog(self):
        """Spawn a QFileDialog to allow user to choose a filename and path."""
        fname = self.filename()
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getSaveFileName(
                self.win,
                "%s - Save As" % QApplication.applicationName(),
                directory,
                "%s (*.nno)" % QApplication.applicationName(),
            )
            self.writeDocumentToFile(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                self.win,
                "%s - Save As" % QApplication.applicationName(),
                directory,
                "%s (*.nno)" % QApplication.applicationName(),
            )
            fdialog.setAcceptMode(QFileDialog.AcceptSave)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            # fdialog.exec_()  # or .show(), or .open()
            self.filesavedialog = fdialog
            self.filesavedialog.filesSelected.connect(self.saveFileDialogCallback)
            fdialog.open()

    def saveFileDialogCallback(self, selected):
        """If the user chose to save, write to that file."""
        if isinstance(selected, QStringList) or isinstance(selected, list):
            fname = selected[0]
        else:
            fname = selected
        if fname.isEmpty() or os.path.isdir(fname):
            return False
        fname = str(fname)
        if not fname.lower().endswith(".nno"):
            fname += ".nno"
        if self.filesavedialog != None:
            self.filesavedialog.filesSelected.disconnect(self.saveFileDialogCallback)
            del self.filesavedialog  # prevents hang
        self.writeDocumentToFile(fname)

    ### document related ###
    def document(self):
        """docstring for document"""
        return self._document

    def setDocument(self, doc):
        """docstring for setDocument"""
        print "in setDocument", doc
        self._document = doc

    ### part related ##
    def activePart(self):
        if self._activePart == None:
            self._activePart = self._document.selectedPart()
        return self._activePart

    def setActivePart(self, part):
        """docstring for setActivePart"""
        self._activePart = part
class DocumentController():
    """
    Connects UI buttons to their corresponding actions in the model.
    """

    ### INIT METHODS ###
    def __init__(self):
        """docstring for __init__"""
        # initialize variables
        self._document = Document()
        self._document.setController(self)
        self._activePart = None
        self._filename = None
        self._fileOpenPath = None  # will be set in _readSettings
        self._hasNoAssociatedFile = True
        self._pathViewInstance = None
        self._sliceViewInstance = None
        self._undoStack = None
        self.win = None
        self.fileopendialog = None
        self.filesavedialog = None

        self.settings = QSettings()
        self._readSettings()

        # call other init methods
        self._initWindow()
        if app().isInMaya():
            self._initMaya()
        app().documentControllers.add(self)

    def _initWindow(self):
        """docstring for initWindow"""
        self.win = DocumentWindow(docCtrlr=self)
        self.win.setWindowIcon(
            QIcon('ui/mainwindow/images/cadnano2-app-icon.png'))
        app().documentWindowWasCreatedSignal.emit(self._document, self.win)
        self._connectWindowSignalsToSelf()
        self.win.show()

    def _initMaya(self):
        """
        Initialize Maya-related state. Delete Maya nodes if there
        is an old document left over from the same session. Set up
        the Maya window.
        """
        # There will only be one document
        if (app().activeDocument and app().activeDocument.win
                and not app().activeDocument.win.close()):
            return
        del app().activeDocument
        app().activeDocument = self

        import maya.OpenMayaUI as OpenMayaUI
        import sip
        ptr = OpenMayaUI.MQtUtil.mainWindow()
        mayaWin = sip.wrapinstance(int(ptr), QMainWindow)
        self.windock = QDockWidget("cadnano")
        self.windock.setFeatures(QDockWidget.DockWidgetMovable
                                 | QDockWidget.DockWidgetFloatable)
        self.windock.setAllowedAreas(Qt.LeftDockWidgetArea
                                     | Qt.RightDockWidgetArea)
        self.windock.setWidget(self.win)
        mayaWin.addDockWidget(Qt.DockWidgetArea(Qt.LeftDockWidgetArea),
                              self.windock)
        self.windock.setVisible(True)

    def destroyDC(self):
        self.disconnectSignalsToSelf()
        if self.win is not None:
            self.win.destroyWin()
            self.win = None

    # end def

    def disconnectSignalsToSelf(self):
        win = self.win
        if win is not None:
            win.actionNew.triggered.disconnect(self.actionNewSlot)
            win.actionOpen.triggered.disconnect(self.actionOpenSlot)
            win.actionClose.triggered.disconnect(self.actionCloseSlot)
            win.actionSave.triggered.disconnect(self.actionSaveSlot)
            win.actionSave_As.triggered.disconnect(self.actionSaveAsSlot)
            win.actionSVG.triggered.disconnect(self.actionSVGSlot)
            win.actionAutoStaple.triggered.disconnect(
                self.actionAutostapleSlot)
            win.actionExportStaples.triggered.disconnect(
                self.actionExportStaplesSlot)
            win.actionPreferences.triggered.disconnect(self.actionPrefsSlot)
            win.actionModify.triggered.disconnect(self.actionModifySlot)
            win.actionNewHoneycombPart.triggered.disconnect(
                self.actionAddHoneycombPartSlot)
            win.actionNewSquarePart.triggered.disconnect(
                self.actionAddSquarePartSlot)
            win.closeEvent = self.windowCloseEventHandler
            win.actionAbout.triggered.disconnect(self.actionAboutSlot)
            win.actionCadnanoWebsite.triggered.disconnect(
                self.actionCadnanoWebsiteSlot)
            win.actionFeedback.triggered.disconnect(self.actionFeedbackSlot)
            win.actionFilterHandle.triggered.disconnect(
                self.actionFilterHandleSlot)
            win.actionFilterEndpoint.triggered.disconnect(
                self.actionFilterEndpointSlot)
            win.actionFilterStrand.triggered.disconnect(
                self.actionFilterStrandSlot)
            win.actionFilterXover.triggered.disconnect(
                self.actionFilterXoverSlot)
            win.actionFilterScaf.triggered.disconnect(
                self.actionFilterScafSlot)
            win.actionFilterStap.triggered.disconnect(
                self.actionFilterStapSlot)
            win.actionRenumber.triggered.disconnect(self.actionRenumberSlot)

    # end def

    def _connectWindowSignalsToSelf(self):
        """This method serves to group all the signal & slot connections
        made by DocumentController"""
        self.win.actionNew.triggered.connect(self.actionNewSlot)
        self.win.actionOpen.triggered.connect(self.actionOpenSlot)
        self.win.actionClose.triggered.connect(self.actionCloseSlot)
        self.win.actionSave.triggered.connect(self.actionSaveSlot)
        self.win.actionSave_As.triggered.connect(self.actionSaveAsSlot)
        self.win.actionSVG.triggered.connect(self.actionSVGSlot)
        self.win.actionAutoStaple.triggered.connect(self.actionAutostapleSlot)
        self.win.actionExportStaples.triggered.connect(
            self.actionExportStaplesSlot)
        self.win.actionPreferences.triggered.connect(self.actionPrefsSlot)
        self.win.actionModify.triggered.connect(self.actionModifySlot)
        self.win.actionNewHoneycombPart.triggered.connect(
            self.actionAddHoneycombPartSlot)
        self.win.actionNewSquarePart.triggered.connect(
            self.actionAddSquarePartSlot)
        self.win.closeEvent = self.windowCloseEventHandler
        self.win.actionAbout.triggered.connect(self.actionAboutSlot)
        self.win.actionCadnanoWebsite.triggered.connect(
            self.actionCadnanoWebsiteSlot)
        self.win.actionFeedback.triggered.connect(self.actionFeedbackSlot)
        self.win.actionFilterHandle.triggered.connect(
            self.actionFilterHandleSlot)
        self.win.actionFilterEndpoint.triggered.connect(
            self.actionFilterEndpointSlot)
        self.win.actionFilterStrand.triggered.connect(
            self.actionFilterStrandSlot)
        self.win.actionFilterXover.triggered.connect(
            self.actionFilterXoverSlot)
        self.win.actionFilterScaf.triggered.connect(self.actionFilterScafSlot)
        self.win.actionFilterStap.triggered.connect(self.actionFilterStapSlot)
        self.win.actionRenumber.triggered.connect(self.actionRenumberSlot)

    ### SLOTS ###
    def undoStackCleanChangedSlot(self):
        """The title changes to include [*] on modification."""
        self.win.setWindowModified(not self.undoStack().isClean())
        self.win.setWindowTitle(self.documentTitle())

    def actionAboutSlot(self):
        """Displays the about cadnano dialog."""
        from ui.dialogs.ui_about import Ui_About
        dialog = QDialog()
        dialogAbout = Ui_About()  # reusing this dialog, should rename
        dialog.setStyleSheet(
            "QDialog { background-image: url(ui/dialogs/images/cadnano2-about.png); background-repeat: none; }"
        )
        dialogAbout.setupUi(dialog)
        dialog.exec_()

    filterList = ["strand", "endpoint", "xover", "virtualHelix"]

    def actionFilterHandleSlot(self):
        """Disables all other selection filters when active."""
        fH = self.win.actionFilterHandle
        fE = self.win.actionFilterEndpoint
        fS = self.win.actionFilterStrand
        fX = self.win.actionFilterXover
        fH.setChecked(True)
        if fE.isChecked():
            fE.setChecked(False)
        if fS.isChecked():
            fS.setChecked(False)
        if fX.isChecked():
            fX.setChecked(False)
        self._document.documentSelectionFilterChangedSignal.emit(
            ["virtualHelix"])

    def actionFilterEndpointSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.actionFilterHandle
        fE = self.win.actionFilterEndpoint
        fS = self.win.actionFilterStrand
        fX = self.win.actionFilterXover
        if fH.isChecked():
            fH.setChecked(False)
        if not fS.isChecked() and not fX.isChecked():
            fE.setChecked(True)
        self._strandFilterUpdate()

    # end def

    def actionFilterStrandSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.actionFilterHandle
        fE = self.win.actionFilterEndpoint
        fS = self.win.actionFilterStrand
        fX = self.win.actionFilterXover
        if fH.isChecked():
            fH.setChecked(False)
        if not fE.isChecked() and not fX.isChecked():
            fS.setChecked(True)
        self._strandFilterUpdate()

    # end def

    def actionFilterXoverSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.actionFilterHandle
        fE = self.win.actionFilterEndpoint
        fS = self.win.actionFilterStrand
        fX = self.win.actionFilterXover
        if fH.isChecked():
            fH.setChecked(False)
        if not fE.isChecked() and not fS.isChecked():
            fX.setChecked(True)
        self._strandFilterUpdate()

    # end def

    def actionFilterScafSlot(self):
        """Remains checked if no other strand-type filter is active."""
        fSc = self.win.actionFilterScaf
        fSt = self.win.actionFilterStap
        if not fSc.isChecked() and not fSt.isChecked():
            fSc.setChecked(True)
        self._strandFilterUpdate()

    def actionFilterStapSlot(self):
        """Remains checked if no other strand-type filter is active."""
        fSc = self.win.actionFilterScaf
        fSt = self.win.actionFilterStap
        if not fSc.isChecked() and not fSt.isChecked():
            fSt.setChecked(True)
        self._strandFilterUpdate()

    # end def

    def _strandFilterUpdate(self):
        win = self.win
        filterList = []
        if win.actionFilterEndpoint.isChecked():
            filterList.append("endpoint")
        if win.actionFilterStrand.isChecked():
            filterList.append("strand")
        if win.actionFilterXover.isChecked():
            filterList.append("xover")
        if win.actionFilterScaf.isChecked():
            filterList.append("scaffold")
        if win.actionFilterStap.isChecked():
            filterList.append("staple")
        self._document.documentSelectionFilterChangedSignal.emit(filterList)

    # end def

    def actionNewSlot(self):
        """
        1. If document is has no parts, do nothing.
        2. If document is dirty, call maybeSave and continue if it succeeds.
        3. Create a new document and swap it into the existing ctrlr/window.
        """
        # clear/reset the view!

        if len(self._document.parts()) == 0:
            return  # no parts
        if self.maybeSave() == False:
            return  # user canceled in maybe save
        else:  # user did not cancel
            if self.filesavedialog != None:
                self.filesavedialog.finished.connect(self.newClickedCallback)
            else:  # user did not save
                self.newClickedCallback()  # finalize new

    def actionOpenSlot(self):
        """
        1. If document is untouched, proceed to open dialog.
        2. If document is dirty, call maybesave and continue if it succeeds.
        Downstream, the file is selected in openAfterMaybeSave,
        and the selected file is actually opened in openAfterMaybeSaveCallback.
        """
        if self.maybeSave() == False:
            return  # user canceled in maybe save
        else:  # user did not cancel
            if hasattr(self, "filesavedialog"):  # user did save
                if self.filesavedialog != None:
                    self.filesavedialog.finished.connect(
                        self.openAfterMaybeSave)
                else:
                    self.openAfterMaybeSave()  # windows
            else:  # user did not save
                self.openAfterMaybeSave()  # finalize new

    def actionCloseSlot(self):
        """This will trigger a Window closeEvent."""
        #print "closing"
        if util.isWindows():
            #print "close win"
            if self.win is not None:
                self.win.close()
            if not app().isInMaya():
                #print "exit app"
                import sys
                sys.exit(1)

    def actionSaveSlot(self):
        """SaveAs if necessary, otherwise overwrite existing file."""
        if self._hasNoAssociatedFile:
            self.saveFileDialog()
            return
        self.writeDocumentToFile()

    def actionSaveAsSlot(self):
        """Open a save file dialog so user can choose a name."""
        self.saveFileDialog()

    def actionSVGSlot(self):
        """docstring for actionSVGSlot"""
        fname = os.path.basename(str(self.filename()))
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()

        fdialog = QFileDialog(self.win,
                              "%s - Save As" % QApplication.applicationName(),
                              directory,
                              "%s (*.svg)" % QApplication.applicationName())
        fdialog.setAcceptMode(QFileDialog.AcceptSave)
        fdialog.setWindowFlags(Qt.Sheet)
        fdialog.setWindowModality(Qt.WindowModal)
        self.svgsavedialog = fdialog
        self.svgsavedialog.filesSelected.connect(self.saveSVGDialogCallback)
        fdialog.open()

    class DummyChild(QGraphicsItem):
        def boundingRect(self):
            return QRect(200, 200)  # self.parentObject().boundingRect()

        def paint(self, painter, option, widget=None):
            pass

    def saveSVGDialogCallback(self, selected):
        if isinstance(selected, (list, tuple)):
            fname = selected[0]
        else:
            fname = selected
        if not fname or fname is None or os.path.isdir(fname):
            return False
        fname = str(fname)
        if not fname.lower().endswith(".svg"):
            fname += ".svg"
        if self.svgsavedialog != None:
            self.svgsavedialog.filesSelected.disconnect(
                self.saveSVGDialogCallback)
            del self.svgsavedialog  # prevents hang
            self.svgsavedialog = None

        generator = QSvgGenerator()
        generator.setFileName(fname)
        generator.setSize(QSize(200, 200))
        generator.setViewBox(QRect(0, 0, 2000, 2000))
        painter = QPainter()

        # Render through scene
        # painter.begin(generator)
        # self.win.pathscene.render(painter)
        # painter.end()

        # Render item-by-item
        painter = QPainter()
        styleOption = QStyleOptionGraphicsItem()
        q = [self.win.pathroot]
        painter.begin(generator)
        while q:
            graphicsItem = q.pop()
            transform = graphicsItem.itemTransform(self.win.sliceroot)[0]
            painter.setTransform(transform)
            if graphicsItem.isVisible():
                graphicsItem.paint(painter, styleOption, None)
                q.extend(graphicsItem.childItems())
        painter.end()

    def actionExportStaplesSlot(self):
        """
        Triggered by clicking Export Staples button. Opens a file dialog to
        determine where the staples should be saved. The callback is
        exportStaplesCallback which collects the staple sequences and exports
        the file.
        """
        # Validate that no staple oligos are loops.
        part = self.activePart()
        stapLoopOlgs = part.getStapleLoopOligos()
        if stapLoopOlgs:
            from ui.dialogs.ui_warning import Ui_Warning
            dialog = QDialog()
            dialogWarning = Ui_Warning()  # reusing this dialog, should rename
            dialog.setStyleSheet(
                "QDialog { background-image: url(ui/dialogs/images/cadnano2-about.png); background-repeat: none; }"
            )
            dialogWarning.setupUi(dialog)

            locs = ", ".join([o.locString() for o in stapLoopOlgs])
            msg = "Part contains staple loop(s) at %s.\n\nUse the break tool to introduce 5' & 3' ends before exporting. Loops have been colored red; use undo to revert." % locs
            dialogWarning.title.setText("Staple validation failed")
            dialogWarning.message.setText(msg)
            for o in stapLoopOlgs:
                o.applyColor(styles.stapColors[0].name())
            dialog.exec_()
            return

        # Proceed with staple export.
        fname = self.filename()
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getSaveFileName(
                self.win, "%s - Export As" % QApplication.applicationName(),
                directory, "(*.csv)")
            self.saveStaplesDialog = None
            self.exportStaplesCallback(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                self.win, "%s - Export As" % QApplication.applicationName(),
                directory, "(*.csv)")
            fdialog.setAcceptMode(QFileDialog.AcceptSave)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.saveStaplesDialog = fdialog
            self.saveStaplesDialog.filesSelected.connect(
                self.exportStaplesCallback)
            fdialog.open()

    # end def

    def actionPrefsSlot(self):
        app().prefsClicked()

    def actionAutostapleSlot(self):
        part = self.activePart()
        if part:
            self.win.pathGraphicsView.setViewportUpdateOn(False)
            part.autoStaple()
            self.win.pathGraphicsView.setViewportUpdateOn(True)

    def actionModifySlot(self):
        """
        Notifies that part root items that parts should respond to modifier
        selection signals. 
        """
        # uncomment for debugging
        # isChecked = self.win.actionModify.isChecked()
        # self.win.pathroot.setModifyState(isChecked)
        # self.win.sliceroot.setModifyState(isChecked)
        if app().isInMaya():
            isChecked = self.win.actionModify.isChecked()
            self.win.pathroot.setModifyState(isChecked)
            self.win.sliceroot.setModifyState(isChecked)
            self.win.solidroot.setModifyState(isChecked)

    def actionAddHoneycombPartSlot(self):
        """docstring for actionAddHoneycombPartSlot"""
        part = self._document.addHoneycombPart()
        self.setActivePart(part)

    def actionAddSquarePartSlot(self):
        """docstring for actionAddSquarePartSlot"""
        part = self._document.addSquarePart()
        self.setActivePart(part)

    def actionRenumberSlot(self):
        coordList = self.win.pathroot.getSelectedPartOrderedVHList()
        part = self.activePart()
        part.renumber(coordList)

    # end def

    ### ACCESSORS ###
    def document(self):
        return self._document

    def window(self):
        return self.win

    def setDocument(self, doc):
        """
        Sets the controller's document, and informs the document that
        this is its controller.
        """
        self._document = doc
        doc.setController(self)

    def activePart(self):
        if self._activePart == None:
            self._activePart = self._document.selectedPart()
        return self._activePart

    def setActivePart(self, part):
        self._activePart = part

    def undoStack(self):
        return self._document.undoStack()

    ### PRIVATE SUPPORT METHODS ###
    def newDocument(self, doc=None, fname=None):
        """Creates a new Document, reusing the DocumentController."""
        self._document.resetViews()
        self._document.removeAllParts()  # clear out old parts
        self._document.undoStack().clear()  # reset undostack
        self._filename = fname if fname else "untitled.json"
        self._hasNoAssociatedFile = fname == None
        self._activePart = None
        self.win.setWindowTitle(self.documentTitle() + '[*]')

    def saveFileDialog(self):
        fname = self.filename()
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getSaveFileName(
                self.win, "%s - Save As" % QApplication.applicationName(),
                directory, "%s (*.json)" % QApplication.applicationName())
            self.writeDocumentToFile(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                self.win, "%s - Save As" % QApplication.applicationName(),
                directory, "%s (*.json)" % QApplication.applicationName())
            fdialog.setAcceptMode(QFileDialog.AcceptSave)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.filesavedialog = fdialog
            self.filesavedialog.filesSelected.connect(
                self.saveFileDialogCallback)
            fdialog.open()

    # end def

    def _readSettings(self):
        self.settings.beginGroup("FileSystem")
        self._fileOpenPath = self.settings.value("openpath", QDir().homePath())
        self.settings.endGroup()

    def _writeFileOpenPath(self, path):
        """docstring for _writePath"""
        self._fileOpenPath = path
        self.settings.beginGroup("FileSystem")
        self.settings.setValue("openpath", path)
        self.settings.endGroup()

    ### SLOT CALLBACKS ###
    def actionNewSlotCallback(self):
        """
        Gets called on completion of filesavedialog after newClicked's 
        maybeSave. Removes the dialog if necessary, but it was probably
        already removed by saveFileDialogCallback.
        """
        if self.filesavedialog != None:
            self.filesavedialog.finished.disconnect(self.actionNewSlotCallback)
            del self.filesavedialog  # prevents hang (?)
            self.filesavedialog = None
        self.newDocument()

    def exportStaplesCallback(self, selected):
        """Export all staple sequences to selected CSV file.

        Args:
            selected (Tuple, List or str): if a List or Tuple, the filename
            should be the first element
        """
        if isinstance(selected, (list, tuple)):
            fname = selected[0]
        else:
            fname = selected
        # Return if fname is '', None, or a directory path
        if not fname or fname is None or os.path.isdir(fname):
            return False
        if not fname.lower().endswith(".txt"):
            fname += ".txt"
        if self.saveStaplesDialog is not None:
            self.saveStaplesDialog.filesSelected.disconnect(
                self.exportStaplesCallback)
            # manual garbage collection to prevent hang (in osx)
            del self.saveStaplesDialog
            self.saveStaplesDialog = None
        # write the file
        ap = self.activePart()
        if ap is not None:
            output = ap.getStapleSequences()
            with open(fname, 'w') as f:
                f.write(output)

    # end def

    def newClickedCallback(self):
        """
        Gets called on completion of filesavedialog after newClicked's 
        maybeSave. Removes the dialog if necessary, but it was probably
        already removed by saveFileDialogCallback.
        """

        if self.filesavedialog != None:
            self.filesavedialog.finished.disconnect(self.newClickedCallback)
            del self.filesavedialog  # prevents hang (?)
            self.filesavedialog = None
        self.newDocument()

    def openAfterMaybeSaveCallback(self, selected):
        """
        Receives file selection info from the dialog created by
        openAfterMaybeSave, following user input.

        Extracts the file name and passes it to the decode method, which
        returns a new document doc, which is then set as the open document
        by newDocument. Calls finalizeImport and disconnects dialog signaling.
        """
        if isinstance(selected, (list, tuple)):
            fname = selected[0]
        else:
            fname = selected
        if fname is None or fname == '' or os.path.isdir(fname):
            return False
        if not os.path.exists(fname):
            return False
        self._writeFileOpenPath(os.path.dirname(fname))
        self.newDocument(fname=fname)

        with io.open(fname, 'r', encoding='utf-8') as fd:
            decode(self._document, fd.read())

        if hasattr(self, "filesavedialog"):  # user did save
            if self.fileopendialog is not None:
                self.fileopendialog.filesSelected.disconnect(
                    self.openAfterMaybeSaveCallback)
            # manual garbage collection to prevent hang (in osx)
            del self.fileopendialog
            self.fileopendialog = None

    def saveFileDialogCallback(self, selected):
        """If the user chose to save, write to that file."""
        if isinstance(selected, (list, tuple)):
            fname = selected[0]
        else:
            fname = selected
        if fname is None or os.path.isdir(fname):
            return False
        if not fname.lower().endswith(".json"):
            fname += ".json"
        if self.filesavedialog is not None:
            self.filesavedialog.filesSelected.disconnect(
                self.saveFileDialogCallback)
            del self.filesavedialog  # prevents hang
            self.filesavedialog = None
        self.writeDocumentToFile(fname)
        self._writeFileOpenPath(os.path.dirname(fname))

    ### EVENT HANDLERS ###
    def windowCloseEventHandler(self, event):
        """Intercept close events when user attempts to close the window."""
        if self.maybeSave():
            event.accept()
            if app().isInMaya():
                self.windock.setVisible(False)
                del self.windock
                self.windock = None
            the_app = app()
            self.destroyDC()
            if the_app.documentControllers:
                the_app.destroyApp()
        else:
            event.ignore()
        self.actionCloseSlot()

    ### FILE INPUT ##
    def documentTitle(self):
        fname = os.path.basename(str(self.filename()))
        if not self.undoStack().isClean():
            fname += '[*]'
        return fname

    def filename(self):
        return self._filename

    def setFilename(self, proposedFName):
        if self._filename == proposedFName:
            return True
        self._filename = proposedFName
        self._hasNoAssociatedFile = False
        self.win.setWindowTitle(self.documentTitle())
        return True

    def openAfterMaybeSave(self):
        """
        This is the method that initiates file opening. It is called by
        actionOpenSlot to spawn a QFileDialog and connect it to a callback
        method.
        """
        path = self._fileOpenPath
        if util.isWindows():  # required for native looking file window#"/",
            fname = QFileDialog.getOpenFileName(
                None, "Open Document", path,
                "cadnano1 / cadnano2 Files (*.nno *.json *.cadnano)")
            self.filesavedialog = None
            self.openAfterMaybeSaveCallback(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                self.win, "Open Document", path,
                "cadnano1 / cadnano2 Files (*.nno *.json *.cadnano)")
            fdialog.setAcceptMode(QFileDialog.AcceptOpen)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.fileopendialog = fdialog
            self.fileopendialog.filesSelected.connect(
                self.openAfterMaybeSaveCallback)
            fdialog.open()

    # end def

    ### FILE OUTPUT ###
    def maybeSave(self):
        """Save on quit, check if document changes have occured."""
        if app().dontAskAndJustDiscardUnsavedChanges:
            return True
        if not self.undoStack().isClean():  # document dirty?
            savebox = QMessageBox(
                QMessageBox.Warning, "Application",
                "The document has been modified.\nDo you want to save your changes?",
                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel,
                self.win,
                Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint | Qt.Sheet)
            savebox.setWindowModality(Qt.WindowModal)
            save = savebox.button(QMessageBox.Save)
            discard = savebox.button(QMessageBox.Discard)
            cancel = savebox.button(QMessageBox.Cancel)
            save.setShortcut("Ctrl+S")
            discard.setShortcut(QKeySequence("D,Ctrl+D"))
            cancel.setShortcut(QKeySequence("C,Ctrl+C,.,Ctrl+."))
            ret = savebox.exec_()
            del savebox  # manual garbage collection to prevent hang (in osx)
            if ret == QMessageBox.Save:
                return self.actionSaveAsSlot()
            elif ret == QMessageBox.Cancel:
                return False
        return True

    def writeDocumentToFile(self, filename=None):
        if filename == None:
            assert (not self._hasNoAssociatedFile)
            filename = self.filename()
        try:
            if util.isWindows():
                filename = filename[0]
            with open(filename, 'w') as f:
                helixOrderList = self.win.pathroot.getSelectedPartOrderedVHList(
                )
                encode(self._document, helixOrderList, f)
        except IOError:
            flags = Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint | Qt.Sheet
            errorbox = QMessageBox(QMessageBox.Critical, "cadnano",
                                   "Could not write to '%s'." % filename,
                                   QMessageBox.Ok, self.win, flags)
            errorbox.setWindowModality(Qt.WindowModal)
            errorbox.open()
            return False
        self.undoStack().setClean()
        self.setFilename(filename)
        return True

    def actionCadnanoWebsiteSlot(self):
        import webbrowser
        webbrowser.open("http://cadnano.org/")

    def actionFeedbackSlot(self):
        import webbrowser
        webbrowser.open("http://cadnano.org/feedback")