I'm trying to make a basic XML story maker in PyQt. So far I've been able to figure out everything my self, but I've run into a bit of a snag. I can't figure out how to load an XML file into a QTreeWidget. I need to preserve the hierarchy, but said hierarchy is seemingly random. I'll post my code, just keep in mind the Open function doesn't work (for obvious reasons):
import sys, os
from PyQt4 import Qt, QtGui, QtCore
class MainWindow( QtGui.QMainWindow ):
width = 640
height = 480
def __init__( self ):
QtGui.QMainWindow.__init__( self )
self.setWindowTitle( "Story Maker" )
self.setWindowIcon( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/text-x-generic.png' ) )
self.fname = None
menubar = self.menuBar()
newAction = QtGui.QAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/document-new.png' ), '&New', self )
newAction.setShortcut( 'Ctrl+N' )
newAction.setStatusTip( 'Start new story' )
saveAction = QtGui.QAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/document-save.png' ), '&Save', self )
saveAction.setShortcut( 'Ctrl+S' )
saveAction.setStatusTip( 'Save story' )
saveAsAction = QtGui.QAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/document-save-as.png' ), '&Save as...', self )
saveAsAction.setShortcut( 'Ctrl+Shift+S' )
saveAsAction.setStatusTip( 'Save story as...' )
openAction = QtGui.QAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/document-open.png' ), '&Open', self )
openAction.setShortcut( 'Ctrl+O' )
openAction.setStatusTip( 'Open story' )
exitAction = QtGui.QAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/process-stop.png' ), '&Exit', self )
exitAction.setShortcut( 'Ctrl+Q' )
exitAction.setStatusTip( 'Exit application' )
fileMenu = menubar.addMenu( '&File' )
fileMenu.addAction( newAction )
fileMenu.addAction( saveAction )
fileMenu.addAction( saveAsAction )
fileMenu.addAction( openAction )
fileMenu.addSeparator();
fileMenu.addAction( exitAction )
main = QtGui.QWidget()
grid = QtGui.QGridLayout()
self.tree = QtGui.QTreeWidget( self )
self.tree.setHeaderLabels( [ 'Text', 'ID' ] )
self.tree.setContextMenuPolicy( QtCore.Qt.CustomContextMenu )
self.tree.customContextMenuRequested.connect( self.contextMenu )
item = QtGui.QTreeWidgetItem( [ 'Main' ], -1 )
self.tree.addTopLevelItem( item )
self.tree.setItemSelected( item, True )
self.addText = QtGui.QPushButton( 'Add Text', self )
addOption = QtGui.QPushButton( 'Add Option', self )
self.remove = QtGui.QPushButton( 'Remove', self )
grid.addWidget( self.tree, 0, 0, 1, 3 )
grid.addWidget( self.addText, 1, 0 )
grid.addWidget( addOption, 1, 1 )
grid.addWidget( self.remove, 1, 2 )
main.setLayout( grid )
self.setCentralWidget( main )
self.resize( MainWindow.width, MainWindow.height )
self.center()
self.statusBar().showMessage( 'Ready' )
newAction.triggered.connect( self.newClicked )
saveAction.triggered.connect( self.saveClicked )
saveAsAction.triggered.connect( self.saveAsClicked )
openAction.triggered.connect( self.openClicked )
exitAction.triggered.connect( QtGui.qApp.quit )
self.connect( self.addText, QtCore.SIGNAL( 'clicked()' ), self.addTextClicked )
self.connect( addOption, QtCore.SIGNAL( 'clicked()' ), self.addOptionClicked )
self.connect( self.remove, QtCore.SIGNAL( 'clicked()' ), self.removeClicked )
self.tree.currentItemChanged.connect( self.onChanged )
def contextMenu( self, position ):
item = self.tree.selectedItems()[0]
menu = QtGui.QMenu()
if( self.addText.isEnabled() ):
menu.addAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/list-add.png' ), self.tr( "Add Text" ) )
menu.addAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/list-add.png' ), self.tr( "Add Option" ) )
if( item.type() != -1 ):
menu.addAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/list-remove.png' ), self.tr( "Delete" ) )
if( item.isExpanded() ):
menu.addAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/go-up.png' ), self.tr( "Collapse" ) )
else:
menu.addAction( QtGui.QIcon( os.path.dirname( os.path.realpath( __file__ ) ) + '/res/icons/go-down.png' ), self.tr( "Expand" ) )
option = menu.exec_( self.tree.viewport().mapToGlobal( position ) )
if( option != None ):
main = self.tree.topLevelItem( 0 )
if( option.text() == "Add Text" ):
new = QtGui.QTreeWidgetItem( [ 'Text', 'DON'T EDIT' ], 0 )
new.setFlags( new.flags() | ( QtCore.Qt.ItemIsEditable ) )
item.addChild( new )
item.setExpanded( True )
if( option.text() == "Add Option" ):
new = QtGui.QTreeWidgetItem( [ 'Text', '0' ], 1 )
new.setFlags( new.flags() | ( QtCore.Qt.ItemIsEditable ) )
item.addChild( new )
item.setExpanded( True )
elif( option.text() == "Delete" ):
main.removeChild( item )
elif( option.text() == "Collapse" ):
item.setExpanded( False )
elif( option.text() == "Expand" ):
item.setExpanded( True )
def newClicked( self ):
answer = QtGui.QMessageBox.question( self, 'Confirm', 'All unsaved data will be lost, continue?', QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No )
if( answer == QtGui.QMessageBox.Yes ):
self.tree.clear()
item = QtGui.QTreeWidgetItem( [ 'Main' ], -1 )
self.tree.addTopLevelItem( item )
self.tree.setItemSelected( item, True )
def save( self ):
f = open( self.fname, 'w' )
main = self.tree.topLevelItem( 0 )
f.write( "<main>
" )
self.recursiveSave( main, f )
f.write( "</main>
" )
f.close()
def recursiveSave( self, main, f ):
if( main.childCount() > 0 ):
for i in xrange( main.childCount() ):
item = main.child( i )
t = item.type()
if( t == 0 ):
f.write( "<text text='" + item.text( 0 ) + "'>
" )
elif( t == 1 ):
f.write( "<option id='" + item.text( 1 ) + "' text='" + item.text( 0 ) + "'>
" )
self.recursiveSave( item, f )
if( t == 0 ):
f.write( "</text>
" )
if( t == 1 ):
f.write( "</option>
" )
def saveClicked( self ):
if( self.fname == None ):
self.saveAsClicked()
else:
self.save()
def saveAsClicked( self ):
dialog = QtGui.QFileDialog( self )
dialog.setAcceptMode( QtGui.QFileDialog.AcceptSave )
dialog.setFileMode( QtGui.QFileDialog.AnyFile )
dialog.setFilter( self.tr( 'Stories (*.story)' ) )
dialog.setDefaultSuffix( self.tr( 'story' ) )
dialog.setConfirmOverwrite( True )
dialog.exec_()
fname = dialog.selectedFiles()[0]
if( fname != None and fname != '' ):
if( not fname.endsWith( '.story' ) ):
dot = fname.lastIndexOf( '.' )
if( dot > -1 ):
fname = fname[0:dot]
fname += '.story'
self.fname = fname
self.save()
def openClicked( self ):
fname = QtGui.QFileDialog.getOpenFileName( self, 'Open file...', '', 'Stories (*.story)' )
if( fname != None and fname != '' ):
self.fname = fname
f = QtCore.QFile( fname )
f.open( QtCore.QIODevice.ReadOnly )
xml = QtCore.QXmlStreamReader( f )
parent = self.tree.topLevelItem( 0 )
previousParent = None
while( xml.atEnd() != True ):
xml.readNext()
if( xml.isStartDocument() ):
continue
if( xml.isStartElement() ):
if( xml.name() == "option" ):
print( "<option id='" + str( xml.attributes().value( 'id' ) ) + "' text='" + str( xml.attributes().value( 'text' ) ) + "'>" )
new = QtGui.QTreeWidgetItem( [ str( xml.attributes().value( 'text' ) ), str( xml.attributes().value( 'id' ) ) ], 1 )
new.setFlags( new.flags() | ( QtCore.Qt.ItemIsEditable ) )
parent.addChild( new )
parent.setExpanded( True )
previousParent = parent
parent = new
if( xml.name() == "text" ):
print( "<text text='" + str( xml.attributes().value( 'text' ) ) + "'>" )
new = QtGui.QTreeWidgetItem( [ str( xml.attributes().value( 'text' ) ), 'DON'T EDIT' ], 0 )
new.setFlags( new.flags() | ( QtCore.Qt.ItemIsEditable ) )
parent.addChild( new )
parent.setExpanded( True )
previousParent = parent
parent = new
elif( xml.isEndElement() ):
if( xml.name() == "option" ):
print( "</option>" )
elif( xml.name() == "text" ):
print( "</text>" )
parent = previousParent
xml.clear()
f.close()
def addTextClicked( self ):
item = self.tree.selectedItems()[0]
new = QtGui.QTreeWidgetItem( [ 'Text', 'DON'T EDIT' ], 0 )
new.setFlags( new.flags() | ( QtCore.Qt.ItemIsEditable ) )
item.addChild( new )
item.setExpanded( True )
def addOptionClicked( self ):
item = self.tree.selectedItems()[0]
new = QtGui.QTreeWidgetItem( [ 'Text', '0' ], 1 )
new.setFlags