class WorldListWidget(QtGui.QDialog, Ui_worldList): def __init__(self, parent=None): super(WorldListWidget, self).__init__(parent, f=Qt.Tool) self.setWindowTitle("World List") self.setWindowModality(Qt.NonModal) self.setupUi(self) self.worldView = None self.chunkLoader = None self.errorWidget = None self.blankWidget = QtGui.QWidget() self.stackedWidget.addWidget(self.blankWidget) self.editButton.clicked.connect(self.editClicked) self.cancelButton.clicked.connect(self.reject) self.showListAgainInput.setEnabled(False) self.viewButton.clicked.connect(self.viewClicked) self.openWorldButton.clicked.connect(self.openWorldClicked) self.repairButton.clicked.connect(self.repairClicked) self.repairButton.setEnabled(False) self.backupButton.clicked.connect(self.backupClicked) self.backupButton.setEnabled(False) self.configureButton.clicked.connect(self.configureClicked) self.chooseSavesFolderButton.clicked.connect(self.chooseSavesFolder) centerWidgetInScreen(self, 0.75) delegate = WorldListItemDelegate() self.worldListView.setItemDelegate(delegate) delegate.setParent(self.worldListView) # PYSIDE-152: get the view widget to the drawPrimitive call self.worldListView.clicked.connect(self.worldListItemClicked) self.worldListView.doubleClicked.connect(self.worldListItemDoubleClicked) self.loadTimer = LoaderTimer(interval=0, timeout=self.loadTimerFired) self.loadTimer.start() self._updateInstalls() try: savesFolders = WorldListSettings.allSavesFolders.value() for filename in savesFolders: if not os.path.isdir(filename): continue dirname, basename = os.path.split(filename) displayName = os.sep.join(dirname.split(os.sep)[-2:] + [basename]) self.savesFolderComboBox.addItem(displayName, filename) except (ValueError, KeyError) as e: log.warn("Failed to load saves folder list.") currentFolder = WorldListSettings.currentSavesFolder.value() if os.path.isdir(currentFolder): index = self.savesFolderComboBox.findData(currentFolder) if index != -1: self.savesFolderComboBox.setCurrentIndex(index) self.savesFolderComboBox.currentIndexChanged.connect(self.savesFolderChanged) self.minecraftInstallBox.currentIndexChanged.connect(self.currentInstallChanged) self.minecraftVersionBox.currentIndexChanged[str].connect(minecraftinstall.currentVersionOption.setValue) self.resourcePackBox.currentIndexChanged.connect(self.resourcePackChanged) self.worldListModel = None self.reloadList() self.reloadRecentWorlds() def currentInstallChanged(self, index): path = self.minecraftInstallBox.itemData(index) minecraftinstall.currentInstallOption.setValue(path) def resourcePackChanged(self, index): if index == 0: minecraftinstall.currentResourcePackOption.setValue("") else: minecraftinstall.currentResourcePackOption.setValue(self.resourcePackBox.currentText()) def _updateInstalls(self): self.minecraftInstallBox.clear() for install in minecraftinstall.GetInstalls().installs: self.minecraftInstallBox.addItem(install.name, install.path) for saveDir in install.getSaveDirs(): self.savesFolderComboBox.addItem(install.name + os.sep + os.path.basename(saveDir), saveDir) for instance in minecraftinstall.GetInstalls().instances: saveDir = instance.saveFileDir self.savesFolderComboBox.addItem(instance.name + os.sep + os.path.basename(saveDir), saveDir) path = minecraftinstall.GetInstalls().selectedInstallPath() index = self.minecraftInstallBox.findData(path) self.minecraftInstallBox.setCurrentIndex(index) self._updateVersionsAndResourcePacks() def _updateVersionsAndResourcePacks(self): self.minecraftVersionBox.clear() self.resourcePackBox.clear() self.resourcePackBox.addItem(self.tr("(No resource pack)")) path = minecraftinstall.currentInstallOption.value() install = minecraftinstall.GetInstalls().getInstall(path) if install: for version in sorted(install.versions, reverse=True): self.minecraftVersionBox.addItem(version) for resourcePack in sorted(install.resourcePacks): self.resourcePackBox.addItem(resourcePack) def chooseSavesFolder(self): startingDir = WorldListSettings.lastChosenSavesFolder.value() if startingDir == '' or not os.path.isdir(startingDir): startingDir = os.path.expanduser(b"~") filename = QtGui.QFileDialog.getExistingDirectory( self, self.tr("Choose Saves Folder"), startingDir ) if filename: log.info("Adding saves folder %s", filename) savesFolders = WorldListSettings.allSavesFolders.value() savesFolders.append(filename) WorldListSettings.allSavesFolders.setValue(savesFolders) dirname, basename = os.path.split(filename) displayName = os.sep.join(dirname.split(os.sep)[-2:] + [basename]) WorldListSettings.lastChosenSavesFolder.setValue(filename) self.savesFolderComboBox.addItem(displayName, filename) self.savesFolderComboBox.setCurrentIndex(self.savesFolderComboBox.count()-1) def reloadRecentWorlds(self): recentWorlds = RecentFilesSetting.value() self.recentWorldsMenu = QtGui.QMenu() def _triggered(f): def triggered(): self.accept() self.editWorldClicked.emit(f) return triggered dead = [] for filename in recentWorlds: if not os.path.exists(filename): dead.append(filename) continue try: displayName, lastPlayed, versionInfo = WorldEditor.getWorldInfo(filename) action = self.recentWorldsMenu.addAction(displayName) action._editWorld = _triggered(filename) action.triggered.connect(action._editWorld) except EnvironmentError as e: log.exception("Failed to load world info") if len(dead): for f in dead: recentWorlds.remove(f) RecentFilesSetting.setValue(recentWorlds) self.recentWorldsButton.setMenu(self.recentWorldsMenu) def savesFolderChanged(self): currentFolder = self.savesFolderComboBox.itemData(self.savesFolderComboBox.currentIndex()) WorldListSettings.currentSavesFolder.setValue(currentFolder) self.reloadList() if len(self.worldListModel.worlds): self.worldListView.setFocus() self.worldListView.setCurrentIndex(self.worldListModel.createIndex(0, 0)) self.showWorld(self.worldListModel.worlds[0][0]) else: self.removeWorldView() def reloadList(self): try: saveFileDir = self.savesFolderComboBox.itemData(self.savesFolderComboBox.currentIndex()) if saveFileDir is None: log.error("No item selected in savesFolderComboBox!!(?)") return if not os.path.isdir(saveFileDir): raise IOError(u"Could not find the Minecraft saves directory!\n\n({0} was not found or is not a directory)".format(saveFileDir)) log.info("Scanning %s for worlds...", saveFileDir) potentialWorlds = os.listdir(saveFileDir) potentialWorlds = [os.path.join(saveFileDir, p) for p in potentialWorlds] worldFiles = [p for p in potentialWorlds if isLevel(AnvilWorldAdapter, p)] self.worldListModel = WorldListModel(worldFiles) self.worldListView.setModel(self.worldListModel) except Exception as e: setWidgetError(self, e, "An error occurred while scanning the saves folder.") def openWorldClicked(self): QtGui.qApp.chooseOpenWorld() _currentFilename = None def worldListItemClicked(self, index): filename = index.data() self.showWorld(filename) def worldListItemDoubleClicked(self, index): row = index.row() self.accept() self.editWorldClicked.emit(self.worldListModel.worlds[row][0]) def showWorld(self, filename): if filename == self._currentFilename: return self._currentFilename = filename self.removeWorldView() try: worldEditor = worldeditor.WorldEditor(filename, readonly=True) except (EnvironmentError, LevelFormatError, zipfile.BadZipfile) as e: self.errorWidget = QtGui.QWidget() setWidgetError(self.errorWidget, e, "An error occurred while reading the world %s." % filename) self.stackedWidget.addWidget(self.errorWidget) self.stackedWidget.setCurrentWidget(self.errorWidget) else: dim = worldEditor.getDimension() self.setWorldView(MinimapWorldView(dim)) self.chunkLoader = ChunkLoader(dim) self.chunkLoader.addClient(self.worldView) self.chunkLoader.chunkCompleted.connect(self.worldView.update) try: try: player = worldEditor.getPlayer() log.info("Centering on single-player player.") except PlayerNotFound: try: center = worldEditor.getWorldMetadata().Spawn log.info("Centering on spawn position.") except AttributeError: log.info("Centering on world center") center = dim.bounds.origin + (dim.bounds.size * 0.5) else: if player.dimName == dim.dimName: center = Vector(*player.Position) self.worldView.centerOnPoint(center) else: center = dim.bounds.origin + (dim.bounds.size * 0.5) self.worldView.centerOnPoint(center) except Exception as e: log.exception("Error while centering view in world list: %s", e) log.info("Switched world view") def setWorldView(self, worldView): if self.worldView: self.removeWorldView() self.worldView = worldView self.stackedWidget.addWidget(worldView) self.stackedWidget.setCurrentWidget(worldView) def removeWorldView(self): self.stackedWidget.setCurrentWidget(self.blankWidget) QtGui.qApp.processEvents() # force repaint of stackedWidget to hide old error widget if self.worldView: log.info("Removing view from WorldListWidget") self.worldView.dealloc() self.stackedWidget.removeWidget(self.worldView) self.worldView.setParent(None) self.worldView = None if self.errorWidget: self.stackedWidget.removeWidget(self.errorWidget) self.errorWidget = None self.chunkLoader = None # ensure WorldView gets freed now and not later # if it is freed later, it will call makeCurrent and ruin another view's render import gc; gc.collect() def hide(self): self.removeWorldView() super(WorldListWidget, self).hide() def close(self): self.removeWorldView() super(WorldListWidget, self).close() def reject(self): self.removeWorldView() super(WorldListWidget, self).reject() def showEvent(self, event): if self.worldListModel and len(self.worldListModel.worlds): self.worldListView.setFocus() self.worldListView.setCurrentIndex(self.worldListModel.createIndex(0, 0)) self.showWorld(self.worldListModel.worlds[0][0]) self.reloadRecentWorlds() @profiler.function("worldListLoadTimer") def loadTimerFired(self): if not self.isVisible(): self.loadTimer.setInterval(1000) return if self.chunkLoader: try: self.chunkLoader.next() self.loadTimer.setInterval(0) except StopIteration: self.loadTimer.setInterval(1000) else: self.loadTimer.setInterval(1000) @property def selectedWorldIndex(self): indexes = self.worldListView.selectedIndexes() if len(indexes): return indexes[0] editWorldClicked = QtCore.Signal(unicode) viewWorldClicked = QtCore.Signal(unicode) repairWorldClicked = QtCore.Signal(unicode) backupWorldClicked = QtCore.Signal(unicode) def editClicked(self): index = self.selectedWorldIndex if index is not None: self.editWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def viewClicked(self): index = self.selectedWorldIndex if index is not None: self.viewWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def repairClicked(self): index = self.selectedWorldIndex if index is not None: self.repairWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def backupClicked(self): index = self.selectedWorldIndex if index is not None: self.backupWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def configureClicked(self): installsWidget = MinecraftInstallsDialog() installsWidget.exec_() self._updateInstalls()
class WorldListWidget(QtGui.QDialog): def __init__(self, parent=None): super(WorldListWidget, self).__init__(parent, f=Qt.Tool) self.setWindowTitle("World List") self.setWindowModality(Qt.NonModal) load_ui('world_list.ui', baseinstance=self) self.worldView = None self.chunkLoader = None self.errorWidget = None self.blankWidget = QtGui.QWidget() self.stackedWidget.addWidget(self.blankWidget) self.editButton.clicked.connect(self.editClicked) self.cancelButton.clicked.connect(self.reject) self.showListAgainInput.setEnabled(False) self.viewButton.clicked.connect(self.viewClicked) self.viewButton.setEnabled(False) self.openWorldButton.clicked.connect(self.openWorldClicked) self.repairButton.clicked.connect(self.repairClicked) self.repairButton.setEnabled(False) self.backupButton.clicked.connect(self.backupClicked) self.backupButton.setEnabled(False) self.configureButton.clicked.connect(self.configureClicked) centerWidgetInScreen(self, 0.75) delegate = WorldListItemDelegate() self.worldListView.setItemDelegate(delegate) delegate.setParent( self.worldListView ) # PYSIDE-152: get the view widget to the drawPrimitive call self.worldListView.clicked.connect(self.worldListItemClicked) self.worldListView.doubleClicked.connect( self.worldListItemDoubleClicked) self.loadTimer = LoaderTimer(interval=0, timeout=self.loadTimerFired) self.loadTimer.start() self._updateInstalls() self.savesFolderComboBox.currentIndexChanged.connect(self.reloadList) self.minecraftInstallBox.currentIndexChanged.connect( minecraftinstall.currentInstallOption.setValue) self.minecraftVersionBox.currentIndexChanged[str].connect( minecraftinstall.currentVersionOption.setValue) self.resourcePackBox.currentIndexChanged.connect( self.resourcePackChanged) self.worldListModel = None self.reloadList() self.reloadRecentWorlds() def resourcePackChanged(self, index): if index == 0: minecraftinstall.currentResourcePackOption.setValue("") else: minecraftinstall.currentResourcePackOption.setValue( self.resourcePackBox.currentText()) def _updateInstalls(self): for install in minecraftinstall.GetInstalls().installs: self.minecraftInstallBox.addItem(install.name) self.minecraftInstallBox.setCurrentIndex( minecraftinstall.GetInstalls().selectedInstallIndex()) self._updateVersionsAndResourcePacks() def _updateVersionsAndResourcePacks(self): self.minecraftVersionBox.clear() self.resourcePackBox.clear() self.resourcePackBox.addItem(self.tr("(No resource pack)")) self.savesFolderComboBox.clear() if self.minecraftInstallBox.count(): install = minecraftinstall.GetInstalls().getInstall( self.minecraftInstallBox.currentIndex()) for version in sorted(install.versions, reverse=True): self.minecraftVersionBox.addItem(version) for resourcePack in sorted(install.resourcePacks): self.resourcePackBox.addItem(resourcePack) for filename in install.getSaveDirs(): self.savesFolderComboBox.addItem( os.path.basename(os.path.dirname(filename)), (filename, None)) for index, instance in enumerate( minecraftinstall.GetInstalls().instances): # xxx instanceID? self.savesFolderComboBox.addItem(instance.name, (instance.saveFileDir, index)) def reloadRecentWorlds(self): recentWorlds = RecentFilesSetting.value() self.recentWorldsMenu = QtGui.QMenu() def _triggered(f): def triggered(): self.accept() self.editWorldClicked.emit(f) return triggered dead = [] for filename in recentWorlds: if not os.path.exists(filename): dead.append(filename) continue try: displayName, lastPlayed, versionInfo = getWorldInfo(filename) action = self.recentWorldsMenu.addAction(displayName) action._editWorld = _triggered(filename) action.triggered.connect(action._editWorld) except EnvironmentError as e: log.exception("Failed to load world info") if len(dead): for f in dead: recentWorlds.remove(f) RecentFilesSetting.setValue(recentWorlds) self.recentWorldsButton.setMenu(self.recentWorldsMenu) def reloadList(self): try: itemData = self.savesFolderComboBox.itemData( self.savesFolderComboBox.currentIndex()) if itemData is None: log.error("No item selected in savesFolderComboBox!!(?)") return saveFileDir, instanceIndex = itemData if instanceIndex is not None: # disable version selector, update resource packs(?) pass if not os.path.isdir(saveFileDir): raise IOError( u"Could not find the Minecraft saves directory!\n\n({0} was not found or is not a directory)" .format(saveFileDir)) log.info("Scanning %s for worlds...", saveFileDir) potentialWorlds = os.listdir(saveFileDir) potentialWorlds = [ os.path.join(saveFileDir, p) for p in potentialWorlds ] worldFiles = [ p for p in potentialWorlds if isLevel(AnvilWorldAdapter, p) ] self.worldListModel = WorldListModel(worldFiles) self.worldListView.setModel(self.worldListModel) if len(self.worldListModel.worlds): self.worldListView.setFocus() self.worldListView.setCurrentIndex( self.worldListModel.createIndex(0, 0)) self.showWorld(self.worldListModel.worlds[0][0]) except EnvironmentError as e: setWidgetError(self, e) def openWorldClicked(self): QtGui.qApp.chooseOpenWorld() _currentFilename = None def worldListItemClicked(self, index): filename = index.data() if filename != self._currentFilename: self._currentFilename = filename self.showWorld(filename) def worldListItemDoubleClicked(self, index): row = index.row() self.accept() self.editWorldClicked.emit(self.worldListModel.worlds[row][0]) def showWorld(self, filename): self.removeWorldView() try: worldEditor = worldeditor.WorldEditor(filename, readonly=True) resLoader = QtGui.qApp.getResourceLoaderForFilename(filename) blockModels = BlockModels(worldEditor.blocktypes, resLoader) textureAtlas = TextureAtlas(worldEditor, resLoader, blockModels) except (EnvironmentError, LevelFormatError, zipfile.BadZipfile) as e: self.errorWidget = QtGui.QWidget() setWidgetError(self.errorWidget, e) self.stackedWidget.addWidget(self.errorWidget) self.stackedWidget.setCurrentWidget(self.errorWidget) else: dim = worldEditor.getDimension() self.setWorldView(MinimapWorldView(dim, textureAtlas)) self.chunkLoader = ChunkLoader(dim) self.chunkLoader.addClient(self.worldView) self.chunkLoader.chunkCompleted.connect(self.worldView.update) try: player = worldEditor.getPlayer() log.info("Centering on single-player player.") except PlayerNotFound: try: center = worldEditor.worldSpawnPosition() log.info("Centering on spawn position.") except AttributeError: log.info("Centering on world center") center = dim.bounds.origin + (dim.bounds.size * 0.5) else: if player.dimName == dim.dimName: center = Vector(*player.Position) self.worldView.centerOnPoint(center) else: center = dim.bounds.origin + (dim.bounds.size * 0.5) self.worldView.centerOnPoint(center) log.info("Switched world view") def setWorldView(self, worldView): if self.worldView: self.removeWorldView() self.worldView = worldView self.stackedWidget.addWidget(worldView) self.stackedWidget.setCurrentWidget(worldView) def removeWorldView(self): self.stackedWidget.setCurrentWidget(self.blankWidget) QtGui.qApp.processEvents( ) # force repaint of stackedWidget to hide old error widget if self.worldView: log.info("Removing view from WorldListWidget") self.worldView.destroy() self.stackedWidget.removeWidget(self.worldView) self.worldView.setParent(None) self.worldView = None if self.errorWidget: self.stackedWidget.removeWidget(self.errorWidget) self.errorWidget = None self.chunkLoader = None def hide(self): self.removeWorldView() super(WorldListWidget, self).hide() def close(self): self.removeWorldView() super(WorldListWidget, self).close() def reject(self): self.removeWorldView() super(WorldListWidget, self).reject() def showEvent(self, event): if self.worldListModel and len(self.worldListModel.worlds): self.worldListView.setFocus() self.worldListView.setCurrentIndex( self.worldListModel.createIndex(0, 0)) self.showWorld(self.worldListModel.worlds[0][0]) self.reloadRecentWorlds() @profiler.function("worldListLoadTimer") def loadTimerFired(self): if not self.isVisible(): self.loadTimer.setInterval(1000) return if self.chunkLoader: try: self.chunkLoader.next() self.loadTimer.setInterval(0) except StopIteration: self.loadTimer.setInterval(1000) else: self.loadTimer.setInterval(1000) @property def selectedWorldIndex(self): indexes = self.worldListView.selectedIndexes() if len(indexes): return indexes[0] editWorldClicked = QtCore.Signal(unicode) viewWorldClicked = QtCore.Signal(unicode) repairWorldClicked = QtCore.Signal(unicode) backupWorldClicked = QtCore.Signal(unicode) def editClicked(self): index = self.selectedWorldIndex if index is not None: self.editWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def viewClicked(self): index = self.selectedWorldIndex if index is not None: self.viewWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def repairClicked(self): index = self.selectedWorldIndex if index is not None: self.repairWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def backupClicked(self): index = self.selectedWorldIndex if index is not None: self.backupWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def configureClicked(self): installsWidget = MinecraftInstallsDialog() installsWidget.exec_() self._updateVersionsAndResourcePacks()
class WorldListWidget(QtGui.QDialog, Ui_worldList): def __init__(self, parent=None): super(WorldListWidget, self).__init__(parent, f=Qt.Tool) self.setWindowTitle("World List") self.setWindowModality(Qt.NonModal) self.setupUi(self) self.worldView = None self.chunkLoader = None self.errorWidget = None self.blankWidget = QtGui.QWidget() self.stackedWidget.addWidget(self.blankWidget) self.editButton.clicked.connect(self.editClicked) self.cancelButton.clicked.connect(self.reject) self.showListAgainInput.setEnabled(False) self.viewButton.clicked.connect(self.viewClicked) self.openWorldButton.clicked.connect(self.openWorldClicked) self.repairButton.clicked.connect(self.repairClicked) self.repairButton.setEnabled(False) self.backupButton.clicked.connect(self.backupClicked) self.backupButton.setEnabled(False) self.configureButton.clicked.connect(self.configureClicked) self.chooseSavesFolderButton.clicked.connect(self.chooseSavesFolder) centerWidgetInScreen(self, 0.75) delegate = WorldListItemDelegate() self.worldListView.setItemDelegate(delegate) delegate.setParent( self.worldListView ) # PYSIDE-152: get the view widget to the drawPrimitive call self.worldListView.clicked.connect(self.worldListItemClicked) self.worldListView.doubleClicked.connect( self.worldListItemDoubleClicked) self.loadTimer = LoaderTimer(interval=0, timeout=self.loadTimerFired) self.loadTimer.start() self._updateInstalls() try: savesFolders = WorldListSettings.allSavesFolders.value() for filename in savesFolders: if not os.path.isdir(filename): continue dirname, basename = os.path.split(filename) displayName = os.sep.join( dirname.split(os.sep)[-2:] + [basename]) self.savesFolderComboBox.addItem(displayName, filename) except (ValueError, KeyError) as e: log.warn("Failed to load saves folder list.") currentFolder = WorldListSettings.currentSavesFolder.value() if os.path.isdir(currentFolder): index = self.savesFolderComboBox.findData(currentFolder) if index != -1: self.savesFolderComboBox.setCurrentIndex(index) self.savesFolderComboBox.currentIndexChanged.connect( self.savesFolderChanged) self.minecraftInstallBox.currentIndexChanged.connect( self.currentInstallChanged) self.minecraftVersionBox.currentIndexChanged[str].connect( minecraftinstall.currentVersionOption.setValue) self.resourcePackBox.currentIndexChanged.connect( self.resourcePackChanged) self.worldListModel = None self.reloadList() self.reloadRecentWorlds() def currentInstallChanged(self, index): path = self.minecraftInstallBox.itemData(index) minecraftinstall.currentInstallOption.setValue(path) def resourcePackChanged(self, index): if index == 0: minecraftinstall.currentResourcePackOption.setValue("") else: minecraftinstall.currentResourcePackOption.setValue( self.resourcePackBox.currentText()) def _updateInstalls(self): self.minecraftInstallBox.clear() for install in minecraftinstall.GetInstalls().installs: self.minecraftInstallBox.addItem(install.name, install.path) for saveDir in install.getSaveDirs(): self.savesFolderComboBox.addItem( install.name + os.sep + os.path.basename(saveDir), saveDir) for instance in minecraftinstall.GetInstalls().instances: saveDir = instance.saveFileDir self.savesFolderComboBox.addItem( instance.name + os.sep + os.path.basename(saveDir), saveDir) path = minecraftinstall.GetInstalls().selectedInstallPath() index = self.minecraftInstallBox.findData(path) self.minecraftInstallBox.setCurrentIndex(index) self._updateVersionsAndResourcePacks() def _updateVersionsAndResourcePacks(self): self.minecraftVersionBox.clear() self.resourcePackBox.clear() self.resourcePackBox.addItem(self.tr("(No resource pack)")) path = minecraftinstall.currentInstallOption.value() install = minecraftinstall.GetInstalls().getInstall(path) if install: for version in sorted(install.versions, reverse=True): self.minecraftVersionBox.addItem(version) for resourcePack in sorted(install.resourcePacks): self.resourcePackBox.addItem(resourcePack) def chooseSavesFolder(self): startingDir = WorldListSettings.lastChosenSavesFolder.value() if startingDir == '' or not os.path.isdir(startingDir): startingDir = os.path.expanduser(b"~") filename = QtGui.QFileDialog.getExistingDirectory( self, self.tr("Choose Saves Folder"), startingDir) if filename: log.info("Adding saves folder %s", filename) savesFolders = WorldListSettings.allSavesFolders.value() savesFolders.append(filename) WorldListSettings.allSavesFolders.setValue(savesFolders) dirname, basename = os.path.split(filename) displayName = os.sep.join(dirname.split(os.sep)[-2:] + [basename]) WorldListSettings.lastChosenSavesFolder.setValue(filename) self.savesFolderComboBox.addItem(displayName, filename) self.savesFolderComboBox.setCurrentIndex( self.savesFolderComboBox.count() - 1) def reloadRecentWorlds(self): recentWorlds = RecentFilesSetting.value() self.recentWorldsMenu = QtGui.QMenu() def _triggered(f): def triggered(): self.accept() self.editWorldClicked.emit(f) return triggered dead = [] for filename in recentWorlds: if not os.path.exists(filename): dead.append(filename) continue try: displayName, lastPlayed, versionInfo = WorldEditor.getWorldInfo( filename) action = self.recentWorldsMenu.addAction(displayName) action._editWorld = _triggered(filename) action.triggered.connect(action._editWorld) except EnvironmentError as e: log.exception("Failed to load world info") if len(dead): for f in dead: recentWorlds.remove(f) RecentFilesSetting.setValue(recentWorlds) self.recentWorldsButton.setMenu(self.recentWorldsMenu) def savesFolderChanged(self): currentFolder = self.savesFolderComboBox.itemData( self.savesFolderComboBox.currentIndex()) WorldListSettings.currentSavesFolder.setValue(currentFolder) self.reloadList() if len(self.worldListModel.worlds): self.worldListView.setFocus() self.worldListView.setCurrentIndex( self.worldListModel.createIndex(0, 0)) self.showWorld(self.worldListModel.worlds[0][0]) else: self.removeWorldView() def reloadList(self): try: saveFileDir = self.savesFolderComboBox.itemData( self.savesFolderComboBox.currentIndex()) if saveFileDir is None: log.error("No item selected in savesFolderComboBox!!(?)") return if not os.path.isdir(saveFileDir): raise IOError( u"Could not find the Minecraft saves directory!\n\n({0} was not found or is not a directory)" .format(saveFileDir)) log.info("Scanning %s for worlds...", saveFileDir) potentialWorlds = os.listdir(saveFileDir) potentialWorlds = [ os.path.join(saveFileDir, p) for p in potentialWorlds ] worldFiles = [ p for p in potentialWorlds if isLevel(AnvilWorldAdapter, p) ] self.worldListModel = WorldListModel(worldFiles) self.worldListView.setModel(self.worldListModel) except Exception as e: setWidgetError( self, e, "An error occurred while scanning the saves folder.") def openWorldClicked(self): QtGui.qApp.chooseOpenWorld() _currentFilename = None def worldListItemClicked(self, index): filename = index.data() self.showWorld(filename) def worldListItemDoubleClicked(self, index): row = index.row() self.accept() self.editWorldClicked.emit(self.worldListModel.worlds[row][0]) def showWorld(self, filename): if filename == self._currentFilename: return self._currentFilename = filename self.removeWorldView() try: worldEditor = worldeditor.WorldEditor(filename, readonly=True) except (EnvironmentError, LevelFormatError, zipfile.BadZipfile) as e: self.errorWidget = QtGui.QWidget() setWidgetError( self.errorWidget, e, "An error occurred while reading the world %s." % filename) self.stackedWidget.addWidget(self.errorWidget) self.stackedWidget.setCurrentWidget(self.errorWidget) else: dim = worldEditor.getDimension() self.setWorldView(MinimapWorldView(dim)) self.chunkLoader = ChunkLoader(dim) self.chunkLoader.addClient(self.worldView) self.chunkLoader.chunkCompleted.connect(self.worldView.update) try: try: player = worldEditor.getPlayer() log.info("Centering on single-player player.") except (PlayerNotFound, NBTFormatError): try: center = worldEditor.getWorldMetadata().Spawn log.info("Centering on spawn position.") except AttributeError: log.info("Centering on world center") center = dim.bounds.origin + (dim.bounds.size * 0.5) else: if player.dimName == dim.dimName: center = Vector(*player.Position) self.worldView.centerOnPoint(center) else: center = dim.bounds.origin + (dim.bounds.size * 0.5) self.worldView.centerOnPoint(center) except Exception as e: log.exception("Error while centering view in world list: %s", e) log.info("Switched world view") def setWorldView(self, worldView): if self.worldView: self.removeWorldView() self.worldView = worldView self.stackedWidget.addWidget(worldView) self.stackedWidget.setCurrentWidget(worldView) def removeWorldView(self): self.stackedWidget.setCurrentWidget(self.blankWidget) QtGui.qApp.processEvents( ) # force repaint of stackedWidget to hide old error widget if self.worldView: log.info("Removing view from WorldListWidget") self.worldView.dealloc() self.stackedWidget.removeWidget(self.worldView) self.worldView.setParent(None) self.worldView = None if self.errorWidget: self.stackedWidget.removeWidget(self.errorWidget) self.errorWidget = None self.chunkLoader = None # ensure WorldView gets freed now and not later # if it is freed later, it will call makeCurrent and ruin another view's render import gc gc.collect() def hide(self): self.removeWorldView() super(WorldListWidget, self).hide() def close(self): self.removeWorldView() super(WorldListWidget, self).close() def reject(self): self.removeWorldView() super(WorldListWidget, self).reject() def showEvent(self, event): if self.worldListModel and len(self.worldListModel.worlds): self.worldListView.setFocus() self.worldListView.setCurrentIndex( self.worldListModel.createIndex(0, 0)) self.showWorld(self.worldListModel.worlds[0][0]) self.reloadRecentWorlds() @profiler.function("worldListLoadTimer") def loadTimerFired(self): if not self.isVisible(): self.loadTimer.setInterval(1000) return if self.chunkLoader: try: self.chunkLoader.next() self.loadTimer.setInterval(0) except StopIteration: self.loadTimer.setInterval(1000) else: self.loadTimer.setInterval(1000) @property def selectedWorldIndex(self): indexes = self.worldListView.selectedIndexes() if len(indexes): return indexes[0] editWorldClicked = QtCore.Signal(unicode) viewWorldClicked = QtCore.Signal(unicode) repairWorldClicked = QtCore.Signal(unicode) backupWorldClicked = QtCore.Signal(unicode) def editClicked(self): index = self.selectedWorldIndex if index is not None: self.editWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def viewClicked(self): index = self.selectedWorldIndex if index is not None: self.viewWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def repairClicked(self): index = self.selectedWorldIndex if index is not None: self.repairWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def backupClicked(self): index = self.selectedWorldIndex if index is not None: self.backupWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def configureClicked(self): installsWidget = MinecraftInstallsDialog() installsWidget.exec_() self._updateInstalls()
class WorldListWidget(QtGui.QDialog): def __init__(self, parent=None, f=0): super(WorldListWidget, self).__init__(parent, f) self.setWindowTitle("World List") self.saveFileDir = None self.worldView = None self.chunkLoader = None self.errorWidget = QtGui.QWidget() load_ui('world_list.ui', baseinstance=self) self.setLayout(Row(self)) self.editButton.clicked.connect(self.editClicked) self.cancelButton.clicked.connect(self.reject) self.showListAgainInput.setEnabled(False) self.viewButton.clicked.connect(self.viewClicked) self.viewButton.setEnabled(False) self.repairButton.clicked.connect(self.repairClicked) self.repairButton.setEnabled(False) self.backupButton.clicked.connect(self.backupClicked) self.backupButton.setEnabled(False) self.configureButton.clicked.connect(self.configureClicked) screen = QtGui.QApplication.desktop().availableGeometry() w = screen.width() h = screen.height() margin = 0.125 r = QtCore.QRect(screen.x() + margin * w, screen.y() + margin * h, w - w * 2 * margin, h - h * 2 * margin) self.setGeometry(r) self.loadTimer = LoaderTimer(interval=0, timeout=self.loadTimerFired) self.loadTimer.start() for install in minecraftinstall.listInstalls(): self.minecraftInstallBox.addItem(install.name) self.minecraftInstallBox.setCurrentIndex( minecraftinstall.selectedInstallIndex()) self._updateVersionsAndResourcePacks() self.reloadList() def _updateVersionsAndResourcePacks(self): install = minecraftinstall.getInstall( self.minecraftInstallBox.currentIndex()) for version in sorted(install.versions, reverse=True): self.minecraftVersionBox.addItem(version) self.resourcePackBox.addItem(self.tr("(No resource pack)")) for resourcePack in sorted(install.resourcePacks): self.resourcePackBox.addItem(resourcePack) self.saveFileDir = install.getSaveFileDir() def getSelectedIVP(self): i = self.minecraftInstallBox.currentIndex() install = minecraftinstall.getInstall(i) v = self.minecraftVersionBox.currentText() if self.resourcePackBox.currentIndex() > 0: p = self.resourcePackBox.currentText() else: p = None return install, v, p def reloadList(self): self.selectedWorldIndex = -1 self.itemWidgets = [] try: if not os.path.isdir(self.saveFileDir): raise IOError( u"Could not find the Minecraft saves directory!\n\n({0} was not found or is not a directory)" .format(self.saveFileDir)) log.info("Scanning %s for worlds...", self.saveFileDir) potentialWorlds = os.listdir(self.saveFileDir) potentialWorlds = [ os.path.join(self.saveFileDir, p) for p in potentialWorlds ] worldFiles = [ p for p in potentialWorlds if isLevel(AnvilWorldAdapter, p) ] worldAdapters = [] for f in worldFiles: try: adapter = findAdapter(f, readonly=True) except Exception as e: log.exception("Could not find adapter for %s: %r", f, e) continue else: worldAdapters.append(adapter) if len(worldAdapters) == 0: raise IOError( "No worlds found! You should probably play Minecraft to create your first world." ) column = QtGui.QVBoxLayout() column.setContentsMargins(0, 0, 0, 0) column.setSpacing(0) worldGroup = QtGui.QButtonGroup(self) #worldGroup.setExclusive(True) for adapter in worldAdapters: item = WorldListItemWidget(adapter) self.itemWidgets.append(item) self.itemWidgets.sort(key=lambda i: i.lastPlayed, reverse=True) for i, item in enumerate(self.itemWidgets): worldGroup.addButton(item, i) column.addWidget(item) item.doubleClicked.connect(self.worldListItemDoubleClicked) worldGroup.buttonClicked[int].connect(self.worldListItemClicked) self.scrollAreaWidgetContents.setLayout(column) except EnvironmentError as e: setWidgetError(self, e) def worldListItemClicked(self, i): if self.selectedWorldIndex == i: return self.selectedWorldIndex = i import gc gc.collect() models = {} try: worldEditor = worldeditor.WorldEditor(self.itemWidgets[i].filename, readonly=True) except (EnvironmentError, LevelFormatError) as e: setWidgetError(self.errorWidget, e) while self.stackedWidget.count(): self.stackedWidget.removeWidget(self.stackedWidget.widget(0)) self.worldViewBox.addWidget(self.errorWidget) else: i, v, p = self.getSelectedIVP() blockModels = models.get(worldEditor.blocktypes) resLoader = i.getResourceLoader(v, p) if blockModels is None: models[worldEditor.blocktypes] = blockModels = BlockModels( worldEditor.blocktypes, resLoader) textureAtlas = TextureAtlas(worldEditor, resLoader, blockModels) dim = worldEditor.getDimension() self.setWorldView(MinimapWorldView(dim, textureAtlas)) self.chunkLoader = ChunkLoader(dim) self.chunkLoader.addClient(self.worldView) self.chunkLoader.chunkCompleted.connect(self.worldView.update) try: player = worldEditor.getPlayer() log.info("Centering on single-player player.") except PlayerNotFound: try: center = worldEditor.worldSpawnPosition() log.info("Centering on spawn position.") except AttributeError: log.info("Centering on world center") center = dim.bounds.origin + (dim.bounds.size * 0.5) else: if player.dimName == dim.dimName: center = Vector(*player.Position) self.worldView.centerOnPoint(center) else: center = dim.bounds.origin + (dim.bounds.size * 0.5) self.worldView.centerOnPoint(center) log.info("Switched world view") def setWorldView(self, worldView): if self.worldView: self.removeWorldView() self.worldView = worldView self.stackedWidget.addWidget(worldView) def removeWorldView(self): if self.worldView: self.worldView.textureAtlas.dispose() self.worldView.destroy() self.worldView.setParent(None) self.stackedWidget.removeWidget(self.worldView) self.worldView = None self.chunkLoader = None def closeEvent(self, event): self.removeWorldView() self.selectedWorldIndex = -1 #import gc; gc.collect() def showEvent(self, event): if len(self.itemWidgets): self.itemWidgets[0].click() def worldListItemDoubleClicked(self): self.editClicked() @profiler.function("worldListLoadTimer") def loadTimerFired(self): if not self.isVisible(): self.loadTimer.setInterval(1000) return if self.chunkLoader: try: self.chunkLoader.next() self.loadTimer.setInterval(0) except StopIteration: self.loadTimer.setInterval(1000) else: self.loadTimer.setInterval(1000) editWorldClicked = QtCore.Signal(unicode) viewWorldClicked = QtCore.Signal(unicode) repairWorldClicked = QtCore.Signal(unicode) backupWorldClicked = QtCore.Signal(unicode) def editClicked(self): self.editWorldClicked.emit( self.itemWidgets[self.selectedWorldIndex].filename) self.accept() def viewClicked(self): self.viewWorldClicked.emit( self.itemWidgets[self.selectedWorldIndex].filename) self.accept() def repairClicked(self): self.repairWorldClicked.emit( self.itemWidgets[self.selectedWorldIndex].filename) self.accept() def backupClicked(self): self.backupWorldClicked.emit( self.itemWidgets[self.selectedWorldIndex].filename) self.accept() def configureClicked(self): installsWidget = MinecraftInstallsDialog() installsWidget.exec_() self._updateVersionsAndResourcePacks()
class WorldListWidget(QtGui.QDialog): def __init__(self, parent=None, f=0): super(WorldListWidget, self).__init__(parent, f) self.setWindowTitle("World List") load_ui('world_list.ui', baseinstance=self) self.worldView = None self.chunkLoader = None self.errorWidget = None self.blankWidget = QtGui.QWidget() self.stackedWidget.addWidget(self.blankWidget) self.editButton.clicked.connect(self.editClicked) self.cancelButton.clicked.connect(self.reject) self.showListAgainInput.setEnabled(False) self.viewButton.clicked.connect(self.viewClicked) self.viewButton.setEnabled(False) self.openWorldButton.clicked.connect(self.openWorldClicked) self.repairButton.clicked.connect(self.repairClicked) self.repairButton.setEnabled(False) self.backupButton.clicked.connect(self.backupClicked) self.backupButton.setEnabled(False) self.configureButton.clicked.connect(self.configureClicked) centerWidgetInScreen(self, 0.75) delegate = WorldListItemDelegate() self.worldListView.setItemDelegate(delegate) delegate.setParent(self.worldListView) # PYSIDE-152: get the view widget to the drawPrimitive call self.worldListView.clicked.connect(self.worldListItemClicked) self.worldListView.doubleClicked.connect(self.worldListItemDoubleClicked) self.loadTimer = LoaderTimer(interval=0, timeout=self.loadTimerFired) self.loadTimer.start() self._updateInstalls() self.savesFolderComboBox.currentIndexChanged.connect(self.reloadList) self.worldListModel = None self.reloadList() self.reloadRecentWorlds() def _updateInstalls(self): for install in minecraftinstall.GetInstalls().installs: self.minecraftInstallBox.addItem(install.name) self.minecraftInstallBox.setCurrentIndex(minecraftinstall.GetInstalls().selectedInstallIndex()) self._updateVersionsAndResourcePacks() def _updateVersionsAndResourcePacks(self): install = minecraftinstall.GetInstalls().getInstall(self.minecraftInstallBox.currentIndex()) self.minecraftVersionBox.clear() for version in sorted(install.versions, reverse=True): self.minecraftVersionBox.addItem(version) self.resourcePackBox.clear() self.resourcePackBox.addItem(self.tr("(No resource pack)")) for resourcePack in sorted(install.resourcePacks): self.resourcePackBox.addItem(resourcePack) self.saveDirs = install.getSaveDirs() self.savesFolderComboBox.clear() for filename in self.saveDirs: self.savesFolderComboBox.addItem(os.path.basename(os.path.dirname(filename)), (filename, None)) for index, instance in enumerate(minecraftinstall.GetInstalls().instances): # xxx instanceID? self.savesFolderComboBox.addItem(instance.name, (instance.saveFileDir, index)) def getSelectedIVP(self): i = self.minecraftInstallBox.currentIndex() install = minecraftinstall.GetInstalls().getInstall(i) v = self.minecraftVersionBox.currentText() if self.resourcePackBox.currentIndex() > 0: p = self.resourcePackBox.currentText() else: p = None return install, v, p def reloadRecentWorlds(self): recentWorlds = RecentFilesSetting.value() self.recentWorldsMenu = QtGui.QMenu() def _triggered(f): def triggered(): self.accept() self.editWorldClicked.emit(f) return triggered for filename in recentWorlds: try: displayName, lastPlayed = getWorldInfo(filename) action = self.recentWorldsMenu.addAction(displayName) action._editWorld = _triggered(filename) action.triggered.connect(action._editWorld) except EnvironmentError as e: log.exception("Failed to load world info") self.recentWorldsButton.setMenu(self.recentWorldsMenu) def reloadList(self): try: itemData = self.savesFolderComboBox.itemData(self.savesFolderComboBox.currentIndex()) if itemData is None: log.error("No item selected in savesFolderComboBox!!(?)") return saveFileDir, instanceIndex = itemData if instanceIndex is not None: # disable version selector, update resource packs(?) pass if not os.path.isdir(saveFileDir): raise IOError(u"Could not find the Minecraft saves directory!\n\n({0} was not found or is not a directory)".format(saveFileDir)) log.info("Scanning %s for worlds...", saveFileDir) potentialWorlds = os.listdir(saveFileDir) potentialWorlds = [os.path.join(saveFileDir, p) for p in potentialWorlds] worldFiles = [p for p in potentialWorlds if isLevel(AnvilWorldAdapter, p)] self.worldListModel = WorldListModel(worldFiles) self.worldListView.setModel(self.worldListModel) if len(self.worldListModel.worlds): self.worldListView.setFocus() self.worldListView.setCurrentIndex(self.worldListModel.createIndex(0, 0)) self.showWorld(self.worldListModel.worlds[0][0]) except EnvironmentError as e: setWidgetError(self, e) def openWorldClicked(self): QtGui.qApp.chooseOpenWorld() _currentFilename = None def worldListItemClicked(self, index): filename = index.data() if filename != self._currentFilename: self._currentFilename = filename self.showWorld(filename) def worldListItemDoubleClicked(self, index): row = index.row() self.accept() self.editWorldClicked.emit(self.worldListModel.worlds[row][0]) def showWorld(self, filename): self.removeWorldView() try: worldEditor = worldeditor.WorldEditor(filename, readonly=True) i, v, p = self.getSelectedIVP() resLoader = i.getResourceLoader(v, p) blockModels = BlockModels(worldEditor.blocktypes, resLoader) textureAtlas = TextureAtlas(worldEditor, resLoader, blockModels) except (EnvironmentError, LevelFormatError, zipfile.BadZipfile) as e: self.errorWidget = QtGui.QWidget() setWidgetError(self.errorWidget, e) self.stackedWidget.addWidget(self.errorWidget) self.stackedWidget.setCurrentWidget(self.errorWidget) else: dim = worldEditor.getDimension() self.setWorldView(MinimapWorldView(dim, textureAtlas)) self.chunkLoader = ChunkLoader(dim) self.chunkLoader.addClient(self.worldView) self.chunkLoader.chunkCompleted.connect(self.worldView.update) try: player = worldEditor.getPlayer() log.info("Centering on single-player player.") except PlayerNotFound: try: center = worldEditor.worldSpawnPosition() log.info("Centering on spawn position.") except AttributeError: log.info("Centering on world center") center = dim.bounds.origin + (dim.bounds.size * 0.5) else: if player.dimName == dim.dimName: center = Vector(*player.Position) self.worldView.centerOnPoint(center) else: center = dim.bounds.origin + (dim.bounds.size * 0.5) self.worldView.centerOnPoint(center) log.info("Switched world view") def setWorldView(self, worldView): if self.worldView: self.removeWorldView() self.worldView = worldView self.stackedWidget.addWidget(worldView) self.stackedWidget.setCurrentWidget(worldView) def removeWorldView(self): self.stackedWidget.setCurrentWidget(self.blankWidget) QtGui.qApp.processEvents() # force repaint of stackedWidget to hide old error widget if self.worldView: log.info("Removing view from WorldListWidget") self.worldView.textureAtlas.dispose() self.worldView.destroy() self.stackedWidget.removeWidget(self.worldView) self.worldView.setParent(None) self.worldView = None if self.errorWidget: self.stackedWidget.removeWidget(self.errorWidget) self.errorWidget = None self.chunkLoader = None def hide(self): self.removeWorldView() super(WorldListWidget, self).hide() def close(self): self.removeWorldView() super(WorldListWidget, self).close() def reject(self): self.removeWorldView() super(WorldListWidget, self).reject() def showEvent(self, event): if self.worldListModel and len(self.worldListModel.worlds): self.worldListView.setFocus() self.worldListView.setCurrentIndex(self.worldListModel.createIndex(0, 0)) self.showWorld(self.worldListModel.worlds[0][0]) self.reloadRecentWorlds() @profiler.function("worldListLoadTimer") def loadTimerFired(self): if not self.isVisible(): self.loadTimer.setInterval(1000) return if self.chunkLoader: try: self.chunkLoader.next() self.loadTimer.setInterval(0) except StopIteration: self.loadTimer.setInterval(1000) else: self.loadTimer.setInterval(1000) @property def selectedWorldIndex(self): indexes = self.worldListView.selectedIndexes() if len(indexes): return indexes[0] editWorldClicked = QtCore.Signal(unicode) viewWorldClicked = QtCore.Signal(unicode) repairWorldClicked = QtCore.Signal(unicode) backupWorldClicked = QtCore.Signal(unicode) def editClicked(self): index = self.selectedWorldIndex if index is not None: self.editWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def viewClicked(self): index = self.selectedWorldIndex if index is not None: self.viewWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def repairClicked(self): index = self.selectedWorldIndex if index is not None: self.repairWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def backupClicked(self): index = self.selectedWorldIndex if index is not None: self.backupWorldClicked.emit(index.data(Qt.DisplayRole)) self.accept() def configureClicked(self): installsWidget = MinecraftInstallsDialog() installsWidget.exec_() self._updateVersionsAndResourcePacks()