Esempio n. 1
0
 def testSaveLoad(self):
     prefs = Preferences('prefstest.conf')
     
     self.assertEqual(None, prefs.btDevice)
     self.assertEqual('bluetooth', prefs.connectionMethod)
     self.assertEqual('', prefs.customDevice)
     self.assertEqual(0, prefs.gammuIndex)
     
     prefs.btDevice = BluetoothDevice('00:00:00:00', 42, 'deviceName', 'serviceName')
     prefs.connectionMethod = 'connection'
     prefs.customDevice = '/dev/rfcomm0'
     prefs.gammuIndex = 2
             
     prefs.save()
     
     prefsLoaded = Preferences('prefstest.conf')
     prefsLoaded.load()
     
     self.assertNotEqual(None, prefsLoaded.btDevice, "Device has not been loaded")
     self.assertEqual('00:00:00:00', prefsLoaded.btDevice.address)
     self.assertEqual(42, prefsLoaded.btDevice.port)
     self.assertEqual('deviceName', prefsLoaded.btDevice.deviceName)
     self.assertEqual('serviceName', prefsLoaded.btDevice.serviceName)
     self.assertEqual('connection', prefsLoaded.connectionMethod)
     self.assertEqual('/dev/rfcomm0', prefsLoaded.customDevice)
     self.assertEqual(2, prefsLoaded.gammuIndex)
Esempio n. 2
0
class Main:
    def __init__(self):
        pygame.init()
        pygame.display.init()
        pygame.font.init()

        self.screen_w = pygame.display.Info().current_w
        self.screen_h = pygame.display.Info().current_h
        self.w = int(self.screen_h * 1.2)
        self.h = int(self.screen_h * 0.8)

        self.preferences = Preferences()
        self.fullscreen = self.preferences.get("fullscreen")
        self.go_mode()

        pygame.mouse.set_visible(False)

        pygame.display.set_caption(Constants.CAPTION)

    def go_mode(self):
        if self.fullscreen:
            self.mode = (self.screen_w, self.screen_h)
            if not self.mode in pygame.display.list_modes():
                self.mode = pygame.display.list_modes()[0]
            self.screen = pygame.display.set_mode(self.mode, pygame.FULLSCREEN)
        else:
            self.mode = (self.w, self.h)
            self.screen = pygame.display.set_mode(self.mode)
        self.unit = int(self.mode[1] / Constants.UNITS)

    def main(self):
        self.boot_screen()
        while True:
            if not self.title_screen():
                break
            if self.preferences.edit_flag:
                self.preferences.save()
                fullscreen = self.preferences.get("fullscreen")
                if self.fullscreen != fullscreen:
                    self.fullscreen = fullscreen
                    self.go_mode()
            else:
                self.play_game()

    def boot_screen(self):
        bs = BootScreen(self.screen, self.unit)
        bs.main()
        return bs.running

    def title_screen(self):
        self.go_mode()
        ts = TitleScreen(self.screen, self.unit, self.preferences)
        ts.main()
        return ts.running

    def play_game(self):
        gm = Game(self.screen, self.unit, self.preferences)
        gm.main()
        return gm.running
Esempio n. 3
0
 def show_preferences(self, widget):
     widget.set_sensitive(False)
     preferences = Preferences()
     response = preferences.run()
     if response == Gtk.ResponseType.ACCEPT:
         preferences.save()
         self.load_preferences()
         self.set_icon(self.is_monitoring)
     preferences.destroy()
     widget.set_sensitive(True)
Esempio n. 4
0
 def show_preferences(self, widget):
     widget.set_sensitive(False)
     preferences = Preferences()
     Keybinder.unbind(self.new_task_keybind)
     Keybinder.unbind(self.show_tasks_keybind)
     response = preferences.run()
     if response == Gtk.ResponseType.ACCEPT:
         preferences.save()
         self.load_preferences()
         self.load_todos()
         self.set_icon()
     widget.set_sensitive(True)
     preferences.destroy()
Esempio n. 5
0
    def show_preferences(self, widget):
        widget.set_sensitive(False)
        preferences_dialog = Preferences()
        response = preferences_dialog.run()
        if response == Gtk.ResponseType.ACCEPT:
            preferences_dialog.save()
            configuration = Configuration()
            preferences = configuration.get('preferences')
            self.theme_light = preferences['theme-light']
            self.application_name = preferences['application_name']

            if self.gotify_client is not None and \
                    self.gotify_client.is_running():
                self.stop()
                self.start()
        preferences_dialog.destroy()
        widget.set_sensitive(True)
Esempio n. 6
0
class MusicGuru(MusicGuruBase, ApplicationBase):
    LOGO_NAME = 'mg_logo'

    def __init__(self):
        appdata = str(
            QDesktopServices.storageLocation(QDesktopServices.DataLocation))
        MusicGuruBase.__init__(self, appdata)
        ApplicationBase.__init__(self)
        if not op.exists(appdata):
            os.makedirs(appdata)
        logging.basicConfig(filename=op.join(appdata, 'debug.log'),
                            level=logging.WARNING)
        self.prefs = Preferences()
        self.prefs.load()
        self.selectedBoardItems = []
        self.selectedLocation = None
        self.mainWindow = MainWindow(app=self)
        self.locationsPanel = LocationsPanel(app=self)
        self.detailsPanel = DetailsPanel(app=self)
        self.ignoreBox = IgnoreBox(app=self)
        self.progress = Progress(self.mainWindow)
        self.aboutBox = AboutBox(self.mainWindow, self)

        self.connect(self.progress, SIGNAL('finished(QString)'),
                     self.jobFinished)
        self.connect(self, SIGNAL('applicationFinishedLaunching()'),
                     self.applicationFinishedLaunching)

    #--- Private
    def _placeDetailsPanel(self):
        # locations panel must be placed first
        if self.detailsPanel.isVisible():
            return
        desktop = QApplication.desktop()
        w = self.locationsPanel.width()
        h = self.detailsPanel.height()
        x = self.locationsPanel.x()
        windowBottom = self.locationsPanel.frameGeometry().y(
        ) + self.locationsPanel.frameGeometry().height()
        y = windowBottom
        self.detailsPanel.move(x, y)
        self.detailsPanel.resize(w, h)

    def _placeIgnoreBox(self):
        if self.ignoreBox.isVisible():
            return
        desktop = QApplication.desktop()
        windowWidth = self.mainWindow.frameGeometry().width()
        frameWidth = self.ignoreBox.frameGeometry().width(
        ) - self.ignoreBox.width()
        w = windowWidth - frameWidth
        h = self.ignoreBox.height()
        x = self.mainWindow.x()
        windowBottom = self.mainWindow.frameGeometry().y(
        ) + self.mainWindow.frameGeometry().height()
        y = min(windowBottom, desktop.height() - h)
        self.ignoreBox.move(x, y)
        self.ignoreBox.resize(w, h)

    def _placeLocationsPanel(self):
        if self.locationsPanel.isVisible():
            return
        desktop = QApplication.desktop()
        w = self.locationsPanel.width()
        windowHeight = self.mainWindow.frameGeometry().height()
        frameHeight = self.locationsPanel.frameGeometry().height(
        ) - self.locationsPanel.height()
        h = windowHeight - frameHeight - self.detailsPanel.frameGeometry(
        ).height()
        windowRight = self.mainWindow.frameGeometry().x(
        ) + self.mainWindow.frameGeometry().width()
        x = min(windowRight, desktop.width() - w)
        y = self.mainWindow.y()
        self.locationsPanel.move(x, y)
        self.locationsPanel.resize(w, h)

    def _setup_as_registered(self):
        self.prefs.registration_code = self.registration_code
        self.prefs.registration_email = self.registration_email
        self.prefs.save()
        self.mainWindow.actionRegister.setVisible(False)
        self.aboutBox.registerButton.hide()
        self.aboutBox.registeredEmailLabel.setText(
            self.prefs.registration_email)

    def _startJob(self, jobid, func):
        title = JOBID2TITLE[jobid]
        try:
            j = self.progress.create_job()
            self.progress.run(jobid, title, func, args=(j, ))
        except job.JobInProgressError:
            msg = "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."
            QMessageBox.information(self.mainWindow, "Action in progress", msg)

    #--- Public
    def addLocation(self, path, name, removeable):
        def do(j):
            MusicGuruBase.AddLocation(self, path, name, removeable, j)

        error_msg = self.CanAddLocation(path, name)
        if error_msg:
            QMessageBox.warning(self.mainWindow, "Add Location", error_msg)
            return
        self._startJob(JOB_ADD, do)

    def addLocationPrompt(self):
        dialog = AddLocationDialog(self)
        result = dialog.exec_()
        if result == QDialog.Accepted:
            self.addLocation(dialog.locationPath, dialog.locationName,
                             dialog.isLocationRemovable)

    def askForRegCode(self):
        if self.reg.ask_for_code():
            self._setup_as_registered()

    def copyOrMove(self, copy):
        def onNeedCd(location):
            # We can't do anything GUI related in a separate thread with Qt. Since copy/move
            # operations are performed asynchronously, the calls made to needCdDialog (created in
            # the main thread) must also be made asynchronously.
            return needCdDialog.askForDiskAsync(location.name)

        def do(j):
            MusicGuruBase.CopyOrMove(self, copy, dirpath, j, onNeedCd)

        needCdDialog = DiskNeededDialog()
        title = "Choose a destination"
        flags = QFileDialog.ShowDirsOnly
        dirpath = str(
            QFileDialog.getExistingDirectory(self.mainWindow, title, '',
                                             flags))
        if dirpath:
            jobid = JOB_MATERIALIZE_COPY if copy else JOB_MATERIALIZE_MOVE
            self._startJob(jobid, do)

    def massRename(self, model, whitespace):
        def do(j):
            self.board.MassRename(model, whitespace, j)

        self._startJob(JOB_MASS_RENAME, do)

    def moveConflicts(self, with_original=False):
        if self.board.MoveConflicts(with_original=with_original) > 0:
            self.emit(SIGNAL('boardChanged()'))
            self.emit(SIGNAL('ignoreBoxChanged()'))

    def moveSelectedToIgnoreBox(self):
        smart_move(self.selectedBoardItems,
                   self.board.ignore_box,
                   allow_merge=True)
        self.emit(SIGNAL('boardChanged()'))
        self.emit(SIGNAL('ignoreBoxChanged()'))

    def removeEmptyFolders(self):
        MusicGuruBase.RemoveEmptyDirs(self)
        self.emit(SIGNAL('boardChanged()'))

    def removeLocation(self, location):
        self.board.RemoveLocation(location)
        location.delete()
        self.emit(SIGNAL('locationsChanged()'))
        self.emit(SIGNAL('boardChanged()'))

    def removeLocationPrompt(self):
        location = self.selectedLocation
        if location is None:
            return
        title = "Remove location"
        msg = "Do you really want to remove location {0}?".format(
            location.name)
        buttons = QMessageBox.Yes | QMessageBox.No
        answer = QMessageBox.question(self.mainWindow, title, msg, buttons,
                                      QMessageBox.Yes)
        if answer != QMessageBox.Yes:
            return
        self.removeLocation(location)

    def renameInRespectiveLocations(self):
        def do(j):
            MusicGuruBase.RenameInRespectiveLocations(self, j)

        self._startJob(JOB_MATERIALIZE_RENAME, do)

    def selectBoardItems(self, items):
        self.selectedBoardItems = items
        self.emit(SIGNAL('boardSelectionChanged()'))

    def selectLocation(self, location):
        self.selectedLocation = location

    def showAboutBox(self):
        self.aboutBox.show()

    def showDetailsPanel(self):
        self._placeLocationsPanel()
        self._placeDetailsPanel()
        self.detailsPanel.show()
        self.detailsPanel.activateWindow()

    def showHelp(self):
        url = QUrl.fromLocalFile(op.join(op.abspath(HELP_PATH), 'intro.htm'))
        QDesktopServices.openUrl(url)

    def showIgnoreBox(self):
        self._placeIgnoreBox()
        self.ignoreBox.show()
        self.ignoreBox.activateWindow()

    def showLocationPanel(self):
        self._placeLocationsPanel()
        self.locationsPanel.show()
        self.locationsPanel.activateWindow()

    def split(self, model, capacity, grouping_level):
        def do(j):
            self.board.Split(model, capacity, grouping_level, j)

        self._startJob(JOB_SPLIT, do)

    def toggleLocation(self, location):
        self.board.ToggleLocation(location)
        self.emit(SIGNAL('locationsChanged()'))
        self.emit(SIGNAL('boardChanged()'))

    def undoSplit(self):
        self.board.Unsplit()
        self.emit(SIGNAL('boardChanged()'))

    def updateCollection(self):
        def do(j):
            self.collection.update_volumes(j)

        self._startJob(JOB_UPDATE, do)

    def updateLocation(self, location):
        def do(j):
            location.update(None, j)

        self._startJob(JOB_UPDATE, do)

    #--- Events
    def applicationFinishedLaunching(self):
        self.reg = Registration(self)
        self.set_registration(self.prefs.registration_code,
                              self.prefs.registration_email)
        if not self.registered and self.unpaid_hours >= 1:
            self.reg.show_nag()
        self.mainWindow.show()
        self.showLocationPanel()
        self.showDetailsPanel()
        self.updateCollection()

    def jobFinished(self, jobid):
        if jobid in (JOB_UPDATE, JOB_ADD):
            self.emit(SIGNAL('locationsChanged()'))
        if jobid in (JOB_MASS_RENAME, JOB_SPLIT):
            self.emit(SIGNAL('boardChanged()'))
        if jobid in (JOB_MATERIALIZE_RENAME, JOB_MATERIALIZE_MOVE):
            self.board.Empty()
            self.emit(SIGNAL('locationsChanged()'))
            self.emit(SIGNAL('boardChanged()'))
            self.emit(SIGNAL('ignoreBoxChanged()'))
Esempio n. 7
0
class MainFrame(wx.Frame):
	def __init__(self, parent):
		wx.Frame.__init__(self, parent, size=(800,600))

		# main menue definition
		fileMenu = wx.Menu()
		menuNew = fileMenu.Append(wx.ID_NEW, '&Import', 'Put directory under checksum control')
		self.Bind(wx.EVT_MENU, self.OnNew, menuNew)
		menuOpen = fileMenu.Append(wx.ID_OPEN, '&Open', 'Open directory under checksum control')
		self.Bind(wx.EVT_MENU, self.OnOpen, menuOpen)
		fileMenu.AppendSeparator()
		menuPreferences = fileMenu.Append(wx.ID_PREFERENCES, '&Preferences\tCtrl+P', 'Show program\'s preferences')
		self.Bind(wx.EVT_MENU, self.OnPreferences, menuPreferences)
		fileMenu.AppendSeparator()
		menuExit = fileMenu.Append(wx.ID_EXIT, 'E&xit', 'Terminate Program')
		self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
		actionMenu = wx.Menu()
		menuCheck = actionMenu.Append(wx.ID_FILE, '&Check\tCtrl+K', 'Check')
		self.Bind(wx.EVT_MENU, self.OnCheck, menuCheck)
		helpMenu = wx.Menu()
		menuAbout = helpMenu.Append(wx.ID_ABOUT, '&About', 'Information about this program')
		self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
		# assemble menu
		menuBar = wx.MenuBar()
		menuBar.Append(fileMenu, '&File')
		menuBar.Append(actionMenu, '&Action')
		menuBar.Append(helpMenu, '&Help')
		self.SetMenuBar(menuBar)

		# current directories and files
		self.UpdateRootDir(None)
		self.preferences = None

		# main window consists of address line and directory listing
		self.address = wx.TextCtrl(self, -1, style=wx.TE_READONLY)
		self.list = ListControlPanel(self)

		sizer = wx.BoxSizer(wx.VERTICAL)
		sizer.Add(self.address, 0, wx.ALL | wx.EXPAND, 5)
		sizer.Add(self.list, 1, wx.ALL | wx.EXPAND, 5)
		self.SetSizer(sizer)

		self.statusbar = self.CreateStatusBar()
		self.statusbar.SetFieldsCount(1)

		self.Show(True)

	def SetAddressLine(self, path=''):
		self.address.SetValue(path)

	def SetStatusBarText(self, text=''):
		self.statusbar.SetStatusText(text)

	def UpdateRootDir(self, rootDir):
		if rootDir is None:
			self.rootDir = None
			self.metaDir = None
			self.dbFile = None
			self.sigFile = None
			self.preferencesFile = None
			self.Title = ProgramName + ' ' + ProgramVersion
		else:
			self.rootDir = rootDir
			self.metaName = '.' + ProgramName
			self.metaDir = os.path.join(self.rootDir, self.metaName)
			self.dbFile = os.path.join(self.metaDir, u'base.sqlite3')
			self.sigFile = os.path.join(self.metaDir, u'base.signature')
			self.preferencesFile = os.path.join(self.metaDir, u'preferences.json')
			self.Title = ProgramName + ' ' + ProgramVersion + \
				' - ' + self.rootDir

	def OnNew(self, event):
		# get a valid path from user
		dirDialog = wx.DirDialog(self, "Choose a directory for import:", \
			style=wx.DD_DEFAULT_STYLE)
		if platform.system() == 'Windows':
			dirDialog.SetPath('D:\\Projects\\treeseal-example') # TESTING
		else:
			dirDialog.SetPath('/home/phil/Projects/treeseal-example') # TESTING

		if dirDialog.ShowModal() == wx.ID_OK:
			self.UpdateRootDir(dirDialog.GetPath())
		else:
			self.UpdateRootDir(None)
			return
		# check pre-conditions
		if os.path.exists(self.metaDir):
			dial = wx.MessageBox('Path "' + self.rootDir + '" already seems to be under checksum control.\n\nDo you still want to continue?', \
				'Warning', wx.YES_NO | wx.ICON_WARNING | wx.NO_DEFAULT)
			if not dial == wx.YES:
				self.UpdateRootDir(None)
				return

		# close eventually existing previous instance
		self.list.ClearInstance()
		self.SetStatusBarText()

		self.Import()

	def OnOpen(self, event):
		# get a valid path from user
		dirDialog = wx.DirDialog(self, "Choose a directory for open:", \
			style=wx.DD_DEFAULT_STYLE)
		if platform.system() == 'Windows':
			dirDialog.SetPath('D:\\Projects\\treeseal-example') # TESTING
		else:
			dirDialog.SetPath('/home/phil/Projects/treeseal-example') # TESTING

		if dirDialog.ShowModal() == wx.ID_OK:
			self.UpdateRootDir(dirDialog.GetPath())
		else:
			self.UpdateRootDir(None)
			return

		# check pre-conditions
		if not os.path.exists(self.metaDir):
			wx.MessageBox('Path "' + self.rootDir + '" is no valid root dir.', \
				'Error', wx.OK | wx.ICON_ERROR)
			self.UpdateRootDir(None)
			return
		if not os.path.exists(self.dbFile):
			wx.MessageBox('Cannot find database file "' + self.dbFile + '".', \
				'Error', wx.OK | wx.ICON_ERROR)
			self.UpdateRootDir(None)
			return
		if not os.path.exists(self.sigFile):
			dial = wx.MessageBox('Cannot find database signature file "' + self.sigFile + '".\n\nUse database without verification?', \
				'Warning', wx.YES_NO | wx.ICON_WARNING | wx.NO_DEFAULT)
			if not dial == wx.YES:
				self.UpdateRootDir(None)
				return
		else:
			cs = Checksum()
			cs.calculateForFile(self.dbFile)
			if not cs.isValidUsingSavedFile(self.sigFile):
				dial = wx.MessageBox('Database or database signature file have been corrupted.\n\nUse database without verification?', \
					'Warning', wx.YES_NO | wx.ICON_WARNING | wx.NO_DEFAULT)
				if not dial == wx.YES:
					self.UpdateRootDir(None)
					return

		# close eventually existing previous instance
		self.list.ClearInstance()
		self.SetStatusBarText()

		# load preferences or create default ones
		self.preferences = Preferences()
		if os.path.exists(self.preferencesFile):
			self.preferences.load(self.preferencesFile)
		else:
			self.preferences.save(self.preferencesFile)


	def OnPreferences(self, event):
		if self.preferences is None:
			wx.MessageBox('Import or Open directory before you can access its preferences.', \
				'Error', wx.OK | wx.ICON_ERROR)
			return
		preferencesDialog = PreferencesDialog(self, self.preferences)
		preferencesDialog.ShowModal()
		self.preferences.save(self.preferencesFile)

	def OnExit(self, event):
		self.Close(True)

	def Import(self):
		# do not care about previous content: reset meta directory and database files
		if os.path.exists(self.metaDir):
			if os.path.exists(self.dbFile):
				os.remove(self.dbFile)
			if os.path.exists(self.sigFile):
				os.remove(self.sigFile)
			if os.path.exists(self.preferencesFile):
				os.remove(self.preferencesFile)
		else:
			os.mkdir(self.metaDir)
		# if on windows platform, hide directory
		if platform.system() == 'Windows':
			os.system('attrib +h "' + self.metaDir + '"')
		self.preferences = Preferences()
		preferencesDialog = PreferencesDialog(self, self.preferences)
		preferencesDialog.ShowModal()
		self.preferences.save(self.preferencesFile)

		try:
			# create trees
			fstree = FilesystemTree(self.rootDir, self.preferences.includes, \
				[ os.path.sep + self.metaName ] + self.preferences.excludes)
			fstree.open()
			dbtree = DatabaseTree(self.dbFile, self.sigFile)
			dbtree.open()
		except MyException as e:
			e.showDialog('Importing ' + self.rootDir)
			return

		try:
			# create progress dialog
			progressDialog = FileProcessingProgressDialog(self, 'Importing ' + self.rootDir)
			progressDialog.Show()
			stats = fstree.getNodeStatistics()
			progressDialog.Init(stats.getNodeCount(), stats.getNodeSize())

			# execute task
			fstree.registerHandlers(progressDialog.SignalNewFile, \
				progressDialog.SignalBytesDone)
			fstree.copyTo(dbtree)
			dbtree.commit()
			fstree.unRegisterHandlers()
		except UserCancelledException:
			progressDialog.SignalFinished()
			return
		except MyException as e:
			progressDialog.Destroy()
			e.showDialog('Importing ' + self.rootDir)
			return

		# signal that we have returned from calculation, either
		# after it is done or after progressDialog signalled that the
		# user stopped the calculation using the cancel button
		progressDialog.SignalFinished()

		self.list.SetInstance(Instance(self.preferences, dbtree, None, None))
		fstree.close()
		self.list.readonly = True

		self.SetStatusBarText('Imported ' + str(stats))

	def OnCheck(self, event):
		# close eventually existing previous instance
		self.list.ClearInstance()
		self.SetStatusBarText()

		try:
			# create trees
			fstree = FilesystemTree(self.rootDir, self.preferences.includes, \
				[ os.path.sep + self.metaName ] + self.preferences.excludes)
			fstree.open()
			dbtree = DatabaseTree(self.dbFile, self.sigFile)
			dbtree.open()
			memtree = MemoryTree()
			memtree.open()
		except MyException as e:
			e.showDialog('Checking ' + self.rootDir)
			return

		try:
			# create progress dialog
			progressDialog = FileProcessingProgressDialog(self, 'Checking ' + self.rootDir)
			progressDialog.Show()
			stats = fstree.getNodeStatistics()
			progressDialog.Init(stats.getNodeCount(), stats.getNodeSize())

			# execute task
			fstree.registerHandlers(progressDialog.SignalNewFile, \
				progressDialog.SignalBytesDone)
			fstree.diff(dbtree, memtree)
			memtree.commit()
			fstree.unRegisterHandlers()
		except UserCancelledException:
			progressDialog.SignalFinished()
			return
		except MyException as e:
			progressDialog.Destroy()
			e.showDialog('Checking ' + self.rootDir)
			return

		# signal that we have returned from calculation, either
		# after it is done or after progressDialog signalled that the
		# user stopped the calcuation using the cancel button
		progressDialog.SignalFinished()

		self.list.SetInstance(Instance(self.preferences, memtree, dbtree, fstree))
		self.list.readonly = False

		self.SetStatusBarText('Checked ' + str(stats))

	def OnAbout(self, event):
		info = wx.AboutDialogInfo()
		#info.SetIcon(wx.Icon('hunter.png', wx.BITMAP_TYPE_PNG))
		info.SetName('treeseal')
		info.SetVersion('3.0')
		info.SetDescription('TreeSeal is a tool for checking the integrity ' + \
			'of a directory structure by keeping checksums and meta ' + \
			'information for each file in a separate database.')
		info.SetCopyright('(C) 2010 - 2013 Philipp Roebrock')
		info.SetWebSite('https://github.com/proebrock/treeseal')
		#info.SetLicence(licence)
		info.AddDeveloper('Philipp Roebrock')
		info.AddDocWriter('Philipp Roebrock')
		wx.AboutBox(info)
Esempio n. 8
0
class TTS_CLI:
    def __init__(self):
        self.preferences = Preferences()

        parser = argparse.ArgumentParser(
            description="Manipulate Tabletop Simulator files")
        parser.add_argument("-d",
                            "--directory",
                            help="Override TTS cache directory")
        parser.add_argument("-l",
                            "--loglevel",
                            help="Set logging level",
                            choices=['debug', 'info', 'warn', 'error'])
        subparsers = parser.add_subparsers(dest='parser',
                                           title='command',
                                           description='Valid commands.')
        subparsers.required = True

        # add list command
        parser_list = subparsers.add_parser('list',
                                            help="List installed mods.",
                                            description='''
    List installed mods.
    If no id is provided, then this will return a list of all installed modules.
    If an id is provided, then this will list the contents of that modules.
    ''')
        group_list = parser_list.add_mutually_exclusive_group()
        group_list.add_argument("-w",
                                "--workshop",
                                action="store_const",
                                metavar='save_type',
                                dest='save_type',
                                const=SaveType.workshop,
                                help="List workshop files (the default).")
        group_list.add_argument("-s",
                                "--save",
                                action="store_const",
                                metavar='save_type',
                                dest='save_type',
                                const=SaveType.save,
                                help="List saves.")
        group_list.add_argument("-c",
                                "--chest",
                                action="store_const",
                                metavar='save_type',
                                dest='save_type',
                                const=SaveType.chest,
                                help="List chest files.")

        parser_list.add_argument("id",
                                 nargs='?',
                                 help="ID of specific mod to list details of.")
        parser_list.set_defaults(func=self.do_list)

        # export command
        parser_export = subparsers.add_parser(
            'export',
            help="Export a mod.",
            description='Export a mod in a format suitible for later import.')
        group_export = parser_export.add_mutually_exclusive_group()
        group_export.add_argument("-w",
                                  "--workshop",
                                  action="store_const",
                                  dest='save_type',
                                  metavar='save_type',
                                  const=SaveType.workshop,
                                  help="ID is of workshop file (the default).")
        group_export.add_argument("-s",
                                  "--save",
                                  action="store_const",
                                  dest='save_type',
                                  metavar='save_type',
                                  const=SaveType.save,
                                  help="ID is of savegame file.")
        group_export.add_argument("-c",
                                  "--chest",
                                  action="store_const",
                                  dest='save_type',
                                  metavar='save_type',
                                  const=SaveType.chest,
                                  help="ID is of chest file.")
        parser_export.add_argument(
            "id", help="ID of mod/name of savegame to export.")
        parser_export.add_argument("-o",
                                   "--output",
                                   help="Location/file to export to.")
        parser_export.add_argument("-f",
                                   "--force",
                                   action="store_true",
                                   help="Force creation of export file.")
        parser_export.add_argument(
            "-d",
            "--download",
            action="store_true",
            help="Attempt to download missing cache files. (EXPERIMENTAL)")
        parser_export.set_defaults(func=self.do_export)

        # import command
        parser_import = subparsers.add_parser(
            'import',
            help="Import a mod.",
            description="Import an previously exported mod.")
        parser_import.add_argument("file", help="Mod pak file to import.")
        parser_import.set_defaults(func=self.do_import)

        # download command
        parser_download = subparsers.add_parser(
            'download',
            help='Download mod files.',
            description=
            'Attempt to download any missing files for an installed mod.')
        group_download = parser_download.add_mutually_exclusive_group()
        group_download.add_argument("-w",
                                    "--workshop",
                                    action="store_const",
                                    dest='save_type',
                                    metavar='save_type',
                                    const=SaveType.workshop,
                                    help="ID is of workshop file.")
        group_download.add_argument("-s",
                                    "--save",
                                    action="store_const",
                                    dest='save_type',
                                    metavar='save_type',
                                    const=SaveType.save,
                                    help="ID is of savegame file.")
        group_download.add_argument("-c",
                                    "--chest",
                                    action="store_const",
                                    dest='save_type',
                                    metavar='save_type',
                                    const=SaveType.chest,
                                    help="ID is of chest file.")
        group_download_target = parser_download.add_mutually_exclusive_group(
            required=True)
        group_download_target.add_argument("-a",
                                           "--all",
                                           action="store_true",
                                           help="Download all.")
        group_download_target.add_argument(
            "id", nargs='?', help="ID of mod/name of savegame to download.")
        parser_download.set_defaults(func=self.do_download)

        # cache command
        parser_cache = subparsers.add_parser('cache',
                                             help='Work with the cache.')
        subparsers_cache = parser_cache.add_subparsers(
            dest='parser_cache',
            title='cache_command',
            description='Valid sub-commands.')
        subparsers_cache.required = True
        parser_cache_create = subparsers_cache.add_parser(
            'create', help='(re)create cache directory')
        parser_cache_create.set_defaults(func=self.do_cache_create)

        # config command
        parser_config = subparsers.add_parser('config',
                                              help='Configure tts manager.')
        subparsers_config = parser_config.add_subparsers(
            dest='parser_config',
            title='config_command',
            description='Valid sub-commands.')
        subparsers_config.required = True
        parser_config_list = subparsers_config.add_parser(
            'list', help='List configuration.')
        parser_config_list.set_defaults(func=self.do_config_list)
        parser_config_validate = subparsers_config.add_parser(
            'validate', help='Validate configuration.')
        parser_config_validate.set_defaults(func=self.do_config_validate)
        parser_config_reset = subparsers_config.add_parser(
            'reset', help='Reset configuration.')
        parser_config_reset.set_defaults(func=self.do_config_reset)
        parser_config_set = subparsers_config.add_parser(
            'set', help='Set configuration parameters.')
        parser_config_set.set_defaults(func=self.do_config_set)
        parser_config_set.add_argument("-m",
                                       "--mod_location",
                                       choices=['documents', 'gamedata'],
                                       help="Where mods are stored.")
        parser_config_set.add_argument("-t",
                                       "--tts_location",
                                       help="TTS Install directory")

        args = parser.parse_args()

        # set logging
        if args.loglevel:
            logmap = {
                'debug': logging.DEBUG,
                'info': logging.INFO,
                'warn': logging.WARN,
                'error': logging.ERROR
            }
            logger().setLevel(logmap[args.loglevel])
        else:
            logger().setLevel(logging.WARN)

        # load filesystem values
        if args.directory:
            self.filesystem = FileSystem(os.path.abspath(args.directory))
        else:
            self.filesystem = self.preferences.get_filesystem()

        if (args.parser == 'list'
                or args.parser == 'export') and not args.save_type:
            # set default
            args.save_type = SaveType.workshop

        if (args.parser == 'config' and args.parser_config == 'set'
                and not args.mod_location and not args.tts_location):
            parser_config_set.error("At least one of -m or -t is required.")

        rc, message = args.func(args)
        if message:
            print(message)
        sys.exit(rc)

    def do_config_set(self, args):
        if args.mod_location:
            self.preferences.locationIsUser = args.mod_location == 'documents'
        if args.tts_location:
            self.preferences.TTSLocation = args.mod_location
        self.preferences.save()
        return 0, "Preferences set"

    def do_config_reset(self, args):
        self.preferences.reset()
        return 0, "Preferences Reset."

    def do_config_list(self, args):
        return 0, self.preferences

    def do_config_validate(self, args):
        if self.preferences.validate():
            return 0, "Configuration validated OK."
        else:
            return 1, "Configuration failed to validate."

    def do_cache_create(self, args):
        try:
            self.filesystem.create_dirs()
        except OSError as exception:
            return 1, "OS error: {0}".format(exception)
        return 0, "All directories created OK."

    def list_by_type(self, save_type):
        result = ""
        for (name, id) in describe_files_by_type(self.filesystem, save_type):
            result += "\n%s (%s)" % (name, id)
        return 0, result

    def list_item(self, data, filename, ident):
        if not data:
            # self.list_installed()
            return
        save = Save(savedata=data,
                    ident=ident,
                    filename=filename,
                    filesystem=self.filesystem)
        return 0, save

    def do_download(self, args):
        successful = True
        if not args.all:
            if not args.save_type:
                args.save_type = self.filesystem.get_json_filename_type(
                    args.id)
            if not args.save_type:
                return 1, "Unable to determine type of id %s" % args.id
            successful = download_file(self.filesystem, args.id,
                                       args.save_type)
        else:
            if args.save_type:
                for ident in self.filesystem.get_filenames_by_type(
                        args.save_type):
                    if not download_file(self.filesystem, ident,
                                         args.save_type):
                        successful = False
                        break
            else:
                for save_type in SaveType:
                    for ident in self.filesystem.get_filenames_by_type(
                            save_type):
                        if not download_file(self.filesystem, ident,
                                             save_type):
                            successful = False
                            break

        if successful:
            return 0, "All files downloaded."
        else:
            return 1, "Some files failed to download."

    def do_list(self, args):
        rc = 0
        result = None

        if not args.id:
            rc, result = self.list_by_type(args.save_type)
        else:
            if not args.save_type:
                args.save_type = self.filesystem.get_json_filename_type(
                    args.id)
            if not args.save_type:
                return 1, "Unable to determine type of id %s" % args.id
            filename = self.filesystem.get_json_filename_for_type(
                args.id, args.save_type)
            data = load_json_file(filename)
            rc, result = self.list_item(data, filename, args.id)
        return rc, result

    def do_export(self, args):
        filename = None
        if args.output:
            if os.path.isdir(args.output):
                filename = os.path.join(args.output, args.id + ".pak")
            else:
                filename = args.output
        else:
            filename = args.id + ".pak"

        data = None
        json_filename = None
        if not args.save_type:
            args.save_type = self.filesystem.get_json_filename_type(args.id)
        if not args.save_type:
            return 1, "Unable to determine type of id %s" % args.id

        json_filename = self.filesystem.get_json_filename_for_type(
            args.id, args.save_type)

        if not json_filename:
            return 1, "Unable to find filename for id %s (wrong -s/-w/-c specified?)" % args.id
        data = load_json_file(json_filename)
        if not data:
            return 1, "Unable to load data for file %s" % json_filename

        save = Save(savedata=data,
                    filename=json_filename,
                    ident=args.id,
                    save_type=args.save_type,
                    filesystem=self.filesystem)
        if not save.isInstalled:
            if not args.download:
                return 1, "Unable to find all urls required by %s. Rerun with -d to try and download them or open it within TTS.\n%s" % (
                    args.id, save)
            else:
                logger().info("Downloading missing files...")
                successful = save.download()
                if successful:
                    logger().info("Files downloaded successfully.")
                else:
                    return 1, "Some files failed to download"
        if os.path.isfile(filename) and not args.force:
            return 1, "%s already exists. Please specify another file or use '-f'" % filename
        logger().info("Exporting json file %s to %s" % (args.id, filename))
        save.export(filename)
        # TODO: exception handling
        return 0, "Exported %s to %s" % (args.id, filename)

    def do_import(self, args):
        return 0, save_helper.importPak(self.filesystem, args.file)
Esempio n. 9
0
class MusicGuru(MusicGuruBase, ApplicationBase):
    LOGO_NAME = 'mg_logo'
    
    def __init__(self):
        appdata = str(QDesktopServices.storageLocation(QDesktopServices.DataLocation))
        MusicGuruBase.__init__(self, appdata)
        ApplicationBase.__init__(self)
        if not op.exists(appdata):
            os.makedirs(appdata)
        logging.basicConfig(filename=op.join(appdata, 'debug.log'), level=logging.WARNING)
        self.prefs = Preferences()
        self.prefs.load()
        self.selectedBoardItems = []
        self.selectedLocation = None
        self.mainWindow = MainWindow(app=self)
        self.locationsPanel = LocationsPanel(app=self)
        self.detailsPanel = DetailsPanel(app=self)
        self.ignoreBox = IgnoreBox(app=self)
        self.progress = Progress(self.mainWindow)
        self.aboutBox = AboutBox(self.mainWindow, self)
        
        self.connect(self.progress, SIGNAL('finished(QString)'), self.jobFinished)
        self.connect(self, SIGNAL('applicationFinishedLaunching()'), self.applicationFinishedLaunching)
    
    #--- Private
    def _placeDetailsPanel(self):
        # locations panel must be placed first
        if self.detailsPanel.isVisible():
            return
        desktop = QApplication.desktop()
        w = self.locationsPanel.width()
        h = self.detailsPanel.height()
        x = self.locationsPanel.x()
        windowBottom = self.locationsPanel.frameGeometry().y() + self.locationsPanel.frameGeometry().height()
        y = windowBottom
        self.detailsPanel.move(x, y)
        self.detailsPanel.resize(w, h)
    
    def _placeIgnoreBox(self):
        if self.ignoreBox.isVisible():
            return
        desktop = QApplication.desktop()
        windowWidth = self.mainWindow.frameGeometry().width()
        frameWidth = self.ignoreBox.frameGeometry().width() - self.ignoreBox.width()
        w = windowWidth - frameWidth
        h = self.ignoreBox.height()
        x = self.mainWindow.x()
        windowBottom = self.mainWindow.frameGeometry().y() + self.mainWindow.frameGeometry().height()
        y = min(windowBottom, desktop.height() - h)
        self.ignoreBox.move(x, y)
        self.ignoreBox.resize(w, h)
    
    def _placeLocationsPanel(self):
        if self.locationsPanel.isVisible():
            return
        desktop = QApplication.desktop()
        w = self.locationsPanel.width()
        windowHeight = self.mainWindow.frameGeometry().height()
        frameHeight = self.locationsPanel.frameGeometry().height() - self.locationsPanel.height()
        h = windowHeight - frameHeight - self.detailsPanel.frameGeometry().height()
        windowRight = self.mainWindow.frameGeometry().x() + self.mainWindow.frameGeometry().width()
        x = min(windowRight, desktop.width() - w)
        y = self.mainWindow.y()
        self.locationsPanel.move(x, y)
        self.locationsPanel.resize(w, h)
    
    def _setup_as_registered(self):
        self.prefs.registration_code = self.registration_code
        self.prefs.registration_email = self.registration_email
        self.prefs.save()
        self.mainWindow.actionRegister.setVisible(False)
        self.aboutBox.registerButton.hide()
        self.aboutBox.registeredEmailLabel.setText(self.prefs.registration_email)
    
    def _startJob(self, jobid, func):
        title = JOBID2TITLE[jobid]
        try:
            j = self.progress.create_job()
            self.progress.run(jobid, title, func, args=(j, ))
        except job.JobInProgressError:
            msg = "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."
            QMessageBox.information(self.mainWindow, "Action in progress", msg)
    
    #--- Public
    def addLocation(self, path, name, removeable):
        def do(j):
            MusicGuruBase.AddLocation(self, path, name, removeable, j)
        
        error_msg = self.CanAddLocation(path, name)
        if error_msg:
            QMessageBox.warning(self.mainWindow, "Add Location", error_msg)
            return
        self._startJob(JOB_ADD, do)
    
    def addLocationPrompt(self):
        dialog = AddLocationDialog(self)
        result = dialog.exec_()
        if result == QDialog.Accepted:
            self.addLocation(dialog.locationPath, dialog.locationName, dialog.isLocationRemovable)
    
    def askForRegCode(self):
        if self.reg.ask_for_code():
            self._setup_as_registered()
    
    def copyOrMove(self, copy):
        def onNeedCd(location):
            # We can't do anything GUI related in a separate thread with Qt. Since copy/move
            # operations are performed asynchronously, the calls made to needCdDialog (created in
            # the main thread) must also be made asynchronously.
            return needCdDialog.askForDiskAsync(location.name)
        
        def do(j):
            MusicGuruBase.CopyOrMove(self, copy, dirpath, j, onNeedCd)
        
        needCdDialog = DiskNeededDialog()
        title = "Choose a destination"
        flags = QFileDialog.ShowDirsOnly
        dirpath = str(QFileDialog.getExistingDirectory(self.mainWindow, title, '', flags))
        if dirpath:
            jobid = JOB_MATERIALIZE_COPY if copy else JOB_MATERIALIZE_MOVE
            self._startJob(jobid, do)
    
    def massRename(self, model, whitespace):
        def do(j):
            self.board.MassRename(model, whitespace, j)
        
        self._startJob(JOB_MASS_RENAME, do)
    
    def moveConflicts(self, with_original=False):
        if self.board.MoveConflicts(with_original=with_original) > 0:
            self.emit(SIGNAL('boardChanged()'))
            self.emit(SIGNAL('ignoreBoxChanged()'))
    
    def moveSelectedToIgnoreBox(self):
        smart_move(self.selectedBoardItems, self.board.ignore_box, allow_merge=True)
        self.emit(SIGNAL('boardChanged()'))
        self.emit(SIGNAL('ignoreBoxChanged()'))
    
    def removeEmptyFolders(self):
        MusicGuruBase.RemoveEmptyDirs(self)
        self.emit(SIGNAL('boardChanged()'))
    
    def removeLocation(self, location):
        self.board.RemoveLocation(location)
        location.delete()
        self.emit(SIGNAL('locationsChanged()'))
        self.emit(SIGNAL('boardChanged()'))
    
    def removeLocationPrompt(self):
        location = self.selectedLocation
        if location is None:
            return
        title = "Remove location"
        msg = "Do you really want to remove location {0}?".format(location.name)
        buttons = QMessageBox.Yes | QMessageBox.No
        answer = QMessageBox.question(self.mainWindow, title, msg, buttons, QMessageBox.Yes)
        if answer != QMessageBox.Yes:
            return
        self.removeLocation(location)
    
    def renameInRespectiveLocations(self):
        def do(j):
            MusicGuruBase.RenameInRespectiveLocations(self, j)
        
        self._startJob(JOB_MATERIALIZE_RENAME, do)
    
    def selectBoardItems(self, items):
        self.selectedBoardItems = items
        self.emit(SIGNAL('boardSelectionChanged()'))
    
    def selectLocation(self, location):
        self.selectedLocation = location
    
    def showAboutBox(self):
        self.aboutBox.show()
    
    def showDetailsPanel(self):
        self._placeLocationsPanel()
        self._placeDetailsPanel()
        self.detailsPanel.show()
        self.detailsPanel.activateWindow()
    
    def showHelp(self):
        url = QUrl.fromLocalFile(op.join(op.abspath(HELP_PATH), 'intro.htm'))
        QDesktopServices.openUrl(url)
    
    def showIgnoreBox(self):
        self._placeIgnoreBox()
        self.ignoreBox.show()
        self.ignoreBox.activateWindow()
    
    def showLocationPanel(self):
        self._placeLocationsPanel()
        self.locationsPanel.show()
        self.locationsPanel.activateWindow()
    
    def split(self, model, capacity, grouping_level):
        def do(j):
            self.board.Split(model, capacity, grouping_level, j)
        
        self._startJob(JOB_SPLIT, do)
    
    def toggleLocation(self, location):
        self.board.ToggleLocation(location)
        self.emit(SIGNAL('locationsChanged()'))
        self.emit(SIGNAL('boardChanged()'))
    
    def undoSplit(self):
        self.board.Unsplit()
        self.emit(SIGNAL('boardChanged()'))
    
    def updateCollection(self):
        def do(j):
            self.collection.update_volumes(j)
        
        self._startJob(JOB_UPDATE, do)
    
    def updateLocation(self, location):
        def do(j):
            location.update(None, j)
        
        self._startJob(JOB_UPDATE, do)
    
    #--- Events
    def applicationFinishedLaunching(self):
        self.reg = Registration(self)
        self.set_registration(self.prefs.registration_code, self.prefs.registration_email)
        if not self.registered and self.unpaid_hours >= 1:
            self.reg.show_nag()
        self.mainWindow.show()
        self.showLocationPanel()
        self.showDetailsPanel()
        self.updateCollection()
    
    def jobFinished(self, jobid):
        if jobid in (JOB_UPDATE, JOB_ADD):
            self.emit(SIGNAL('locationsChanged()'))
        if jobid in (JOB_MASS_RENAME, JOB_SPLIT):
            self.emit(SIGNAL('boardChanged()'))
        if jobid in (JOB_MATERIALIZE_RENAME, JOB_MATERIALIZE_MOVE):
            self.board.Empty()
            self.emit(SIGNAL('locationsChanged()'))
            self.emit(SIGNAL('boardChanged()'))
            self.emit(SIGNAL('ignoreBoxChanged()'))