Exemple #1
0
	def run(self):
		self.waitCondition.acquire()
		while not self.shouldStop():
			# Allow the subclass to update the GUI
			log('StateMachine: visiting state ' + str(self.state))
			self.step(self.state)
			
			# Compute the next state
			next = (self.state.toSeconds() + self.delay) % (24 * 60 * 60)
			self.state = Time.fromSeconds(next)
			
			# Calculate the delay
			now = datetime.datetime.now()
			seconds = now.hour * 60 * 60 + now.minute * 60 + now.second + now.microsecond / 1000000.0
			delay = self.state.toSeconds() - seconds
			if delay < 0:
				delay = delay + 24 * 60 * 60
			log('Sleeping for %f seconds' % delay)
			self.waitCondition.wait(delay)
		#except:
		#	log('Exception thrown in StateMachine thread')
		#	self.waitCondition.release()
		#	self.stop()
		log('StateMachine shutting down')
		self.cleanup()
		self.waitCondition.release()
		log('StateMachine finished shutting down')
Exemple #2
0
	def add(self, timeObject, timeString, rule = None):
		'''
		timeObject - the id found in the <time> tag
		timeString - the text of the <time> tag
		rule - this parameter is purely for recursion. Because the rule has
		already been created, it can be passed in as a parameter to avoid
		creating an unnecessary copy. Recursion is only employed if the rule is
		a constant (no symbols); otherwise, a modified timeObject will
		invalidate the rule.
		'''
		log('Adding rule %s: ' % str(timeObject) + timeString.encode('utf-8'))
		
		# Use 1-based time for rule resolution
		timeObject.hours = (timeObject.hours - 1) % (24 if self.use24 else 12) + 1
		
		# Create the rule
		if not rule:
			rule = [self.createToken(word, timeObject) for word in timeString.split(' ')]
		
		# If rule contains no symbols, only constants, then only add rule for its hour
		isConst = self.isConstant(rule)
		
		# Now that the rule is created, we can apply the default duration if
		# the rule is all constant
		if isConst and not timeObject.duration:
			timeObject.duration = Time.fromSeconds(self.defaultDuration)
		
		for i in range(len(self.rules)):
			hour = (i - 1) % (24 if self.use24 else 12) + 1 # make hour 1-based
			
			# Constant times are only for a single hour
			if isConst and hour != timeObject.hours:
				continue
			
			self.rules[i] = self.insert(self.rules[i], rule, timeObject, hour)
			
			if isConst:
				# However, if the duration pushes the rule into the next hour,
				# clone the rule (with an appropriately shorter duration) and
				# recursively add to the next hour as well.
				hourBoundary = (timeObject.hours + 1) * 3600
				if timeObject.end() > hourBoundary:
					# Start at the beginning of the next hour
					nextTime = Time(timeObject.hours + 1, 0, 0)
					nextTime.duration = Time.fromSeconds(timeObject.end() - hourBoundary)
					nextTime.useSeconds = timeObject.useSeconds # For cosmetics
					self.add(nextTime, timeString, rule)
				break
Exemple #3
0
	def spin(self):
		self.qlockThread.start()
		#self.spriteThread.start()
		
		# Give the solvers time to do their work, so that when the window fades
		# in, the solution will already have been highlighted.
		# I noticed problems as high as 10ms, so use 25ms.
		if not self.config.ssMode:
			xbmc.sleep(25)
		
		self.window.doModal()
		try:
			self.qlockThread.stop()
			#self.spriteThread.stop()
		except:
			log('Error occurred while stopping background threads')
Exemple #4
0
	def __unicode__(self):
		'''
		Convert the symbol to a string. If the stringTable entry for the
		calculated number has a space, then the returned value will also have
		a space and therefore will need to be split. If the string table entry
		does not exist, an empty string is returned.
		'''
		if self.unit == 'h':
			id = self.transform(self.timeSource.hours)
		elif self.unit == 'm':
			id = self.transform(self.timeSource.minutes)
		elif self.unit == 's':
			id = self.transform(self.timeSource.seconds)
		# If id is not defined, return an empty string
		if id not in self.stringTable:
			log('Error: no string defined for id %d' % id)
		return self.stringTable.get(id, '')
Exemple #5
0
	def lookupRecursive(self, node, time):
		'''Basic linked list node transversal'''
		if node == None:
			log('ERROR: No node, returning []')
			return []
		
		if node.next == None:
			# Base case: next node is null, return this one
			return node.rule
		
		# Compare minutes and seconds (by making the hours equal)
		tempTime = Time(node.next.time.hours, time.minutes, time.seconds)
		if tempTime.toSeconds() < node.next.time.toSeconds():
			# Base case: time comes before node.time, so return node.rule
			return node.rule
		
		return self.lookupRecursive(node.next, time)
Exemple #6
0
	def step(self, time):
		# Ask the solver for the time
		log('Solving for the current time (%s)' % str(time))
		solution = self.solver.resolveTime(time)
		solutionUTF8 = [uni.encode('utf-8') for uni in solution]
		log('Solution: ' + str(solutionUTF8))
		
		truthMatrix = createTruthMatrix(self.layout.height, self.layout.width)
		success = self.highlight(self.layout.matrix, truthMatrix, solution)
		if not success:
			log('Unable to highlight solution. Reattempting with no spaces between words')
			truthMatrix = createTruthMatrix(self.layout.height, self.layout.width)
			success = self.highlight(self.layout.matrix, truthMatrix, solution, False)
			if success:
				log('Success')
			else:
				log('Failed to highlight solution again. Drawing best attempt')
		
		# Draw the result
		self.window.drawMatrix(truthMatrix)
Exemple #7
0
	def __init__(self, file):
		log('Using theme: ' + os.path.basename(file))
		try:
			root = ElementTree.parse(file).getroot()
		except:
			log('Error parsing theme file!')
			raise # Let the user see the error
		try:
			self.active = root.find('active').text
			self.inactive = root.find('inactive').text
			imgNode = root.find('image')
			if imgNode != None:
				self.image = imgNode.text
				# Width and height default to fullscreen if unspecified
				self.imageWidth = int(imgNode.attrib['width']) if 'width' in imgNode.attrib else 1280
				self.imageHeight = int(imgNode.attrib['height']) if 'height' in imgNode.attrib else 720
				# If an image was found, background is optional
				if root.find('background') != None:
					self.background = root.find('background').text
				else:
					self.background = '00000000' # fully transparent
			else:
				# If an image wasn't found, background is mandatory
				self.background = root.find('background').text
				self.image = None
				self.imageWidth = 1280
				self.imageHeight = 720
			# In SS mode, the background is black so ignore the alpha channel
			if xbmc.getCondVisibility('System.ScreenSaverActive'):
				# Proceed if all chars are hex
				if self.isHexColor(self.background):
					self.background = 'ff' + self.background[2:]
		except:
			log('Error parsing theme file!')
			sys.exit()
Exemple #8
0
	def __init__(self, window, layout):
		delay = self.calcDelay(layout)
		super(QlockThread, self).__init__(delay)
		self.window = window
		
		# Use a lowercase matrix for comparison
		self.layout = deepcopy(layout)
		for row in range(self.layout.height):
			for col in range(self.layout.width):
				self.layout.matrix[row][col] = self.layout.matrix[row][col].lower()
		
		log('Creating the solver')
		now = datetime.datetime.now()
		start = now.hour * 60 * 60 + now.minute * 60 + now.second + now.microsecond / 1000000.0
		
		# Let the solver know about the default delay. It will need this
		# information once it has parsed a times string into tokens.
		self.solver = solver.Solver(layout, delay)
		
		nodes = self.solver.countNodes()
		now = datetime.datetime.now()
		stop = now.hour * 60 * 60 + now.minute * 60 + now.second + now.microsecond / 1000000.0
		log('Solver created in %f seconds with %d nodes and %d rules' % (stop - start, nodes, len(layout.times)))
Exemple #9
0
	def changeTheme(self):
		self.config.loadNextTheme()
		background = self.getControl(gui.CONTROL_BACKGROUND)
		if self.config.theme.isHexColor(self.config.theme.background):
			# setColorDiffuse() expects 0xAARRGGBB
			background.setColorDiffuse('0x' + self.config.theme.background)
		else:
			# As of https://github.com/xbmc/xbmc/commit/44472bc64, setColorDiffuse()
			# can accept a skin color as well as a hex code. If your XBMC is older,
			# the background will be transparent.
			background.setColorDiffuse(self.config.theme.background)
		
		# Always complain about XBMC's missing features
		log('Unable to change text color: Not implemented in XBMC!')
		
		image = self.getControl(gui.CONTROL_IMAGE)
		if self.config.theme.image:
			image.setImage(os.path.join(self.config.themeDir, self.config.theme.image))
			image.setWidth(self.config.theme.imageWidth)
			image.setHeight(self.config.theme.imageHeight)
			image.setVisible(True)
		else:
			image.setVisible(False)
Exemple #10
0
    def changeTheme(self):
        self.config.loadNextTheme()
        background = self.getControl(gui.CONTROL_BACKGROUND)
        if self.config.theme.isHexColor(self.config.theme.background):
            # setColorDiffuse() expects 0xAARRGGBB
            background.setColorDiffuse('0x' + self.config.theme.background)
        else:
            # As of https://github.com/xbmc/xbmc/commit/44472bc64, setColorDiffuse()
            # can accept a skin color as well as a hex code. If your XBMC is older,
            # the background will be transparent.
            background.setColorDiffuse(self.config.theme.background)

        # Always complain about XBMC's missing features
        log('Unable to change text color: Not implemented in XBMC!')

        image = self.getControl(gui.CONTROL_IMAGE)
        if self.config.theme.image:
            image.setImage(
                os.path.join(self.config.themeDir, self.config.theme.image))
            image.setWidth(self.config.theme.imageWidth)
            image.setHeight(self.config.theme.imageHeight)
            image.setVisible(True)
        else:
            image.setVisible(False)
Exemple #11
0
	def __init__(self, config):
		# Test to see if the window is already open
		if xbmc.getCondVisibility('Window.IsVisible(%s)' % WINDOW_ID):
			log('Bailing out (window already exists?)')
			sys.exit()
		
		# Generate and pretty-print the GUI XML
		windowGUI = gui.Window(config.layout, config.theme, config.ssMode)
		windowXML = windowGUI.toXMLPrettyPlease()
		
		# Create a mimic directory that we can write to
		skinDir = os.path.join(config.profile, 'resources', 'skins', 'Default', '720p')
		if not os.path.isdir(skinDir):
			os.makedirs(skinDir)
		ElementTree.ElementTree(windowXML).write(os.path.join(skinDir, 'unqlocked.xml'))
		log('Wrote ' + os.path.join(skinDir, 'unqlocked.xml'))
		
		# Copy our images over to the new folder
		mediaDir = os.path.join(config.profile, 'resources', 'skins', 'Default', 'media')
		if not os.path.isdir(mediaDir):
			os.makedirs(mediaDir)
		# Allow layout to specify background images
		images = ['unqlocked-1px-white.png']
		if config.theme.image:
			images.append(config.theme.image)
		for image in images:
			imgPath = os.path.join(config.themeDir, image)
			newPath = os.path.join(mediaDir, image)
			if not os.path.exists(newPath):
				shutil.copyfile(imgPath, newPath)
				log('Wrote ' + newPath)
		
		# Now create the GUI window
		self.window = window.UnqlockedWindow('unqlocked.xml', config.profile, 'Default')
		self.window.setConfig(config)
		self.window.setLayoutCallback(self.layoutCallback)
		self.window.setDemoCallback(self.demoCallback)
		self.window.drawBackground()
		
		# Create the threads
		self.qlockThread = statemachine.QlockThread(self.window, config.layout)
		#self.spriteThread = statemachine.SpriteThread(self.window, config) # not implemented yet
		
		self.config = config
Exemple #12
0
 def __init__(self, file):
     log('Using theme: ' + os.path.basename(file))
     try:
         root = ElementTree.parse(file).getroot()
     except:
         log('Error parsing theme file!')
         raise  # Let the user see the error
     try:
         self.active = root.find('active').text
         self.inactive = root.find('inactive').text
         imgNode = root.find('image')
         if imgNode != None:
             self.image = imgNode.text
             # Width and height default to fullscreen if unspecified
             self.imageWidth = int(imgNode.attrib['width']
                                   ) if 'width' in imgNode.attrib else 1280
             self.imageHeight = int(imgNode.attrib['height']
                                    ) if 'height' in imgNode.attrib else 720
             # If an image was found, background is optional
             if root.find('background') != None:
                 self.background = root.find('background').text
             else:
                 self.background = '00000000'  # fully transparent
         else:
             # If an image wasn't found, background is mandatory
             self.background = root.find('background').text
             self.image = None
             self.imageWidth = 1280
             self.imageHeight = 720
         # In SS mode, the background is black so ignore the alpha channel
         if xbmc.getCondVisibility('System.ScreenSaverActive'):
             # Proceed if all chars are hex
             if self.isHexColor(self.background):
                 self.background = 'ff' + self.background[2:]
     except:
         log('Error parsing theme file!')
         sys.exit()
Exemple #13
0
    def __init__(self, file):
        log('Using layout: ' + os.path.basename(file))
        try:
            root = ElementTree.parse(file).getroot()
        except:
            log('Error parsing layout file!')
            raise  # Let the user see the error
        try:
            background = root.find('background')
            self.height = int(background.attrib['height'])
            self.width = int(background.attrib['width'])
        except:
            log('Error: <background> tag missing/incorrect!')
            sys.exit()

        self.matrix = []
        entities = [
            char.strip().upper() for char in background.text.split(',')
        ]
        if (self.height * self.width > len(entities)):
            log('Error: Too many characters in background (expecting %d, found %d)' % \
             (self.height * self.width, len(entities)))
            sys.exit()
        elif (self.height * self.width < len(entities)):
            log('Error: Too few characters in background (expecting %d, found %d)' % \
             (self.height * self.width, len(entities)))
            sys.exit()
        for i in range(self.height):
            self.matrix.append(entities[i * self.width:(i + 1) * self.width])

        timesNode = root.find('times')
        if timesNode == None:
            log('Error: <times> node not found!')
            sys.exit()
        if 'use24' in timesNode.attrib and timesNode.attrib['use24'] == 'true':
            self.use24 = True
        else:
            self.use24 = False

        self.times = {}
        for time in timesNode.findall('time'):
            if 'id' not in time.attrib:
                log('Warning: found <time> tag with no "id" attribute')
                continue
            key = Time(time.attrib['id'])
            # If set, store the duration with the key
            if 'duration' in time.attrib:
                key.duration = Time(time.attrib['duration'])
            self.times[key] = time.text.lower()
        if len(self.times) == 0:
            log('Error: no <time> tags found!')
            sys.exit()

        self.strings = {}
        stringsNode = root.find('strings')
        if stringsNode == None:
            log('Error: <strings> node not found!')
            sys.exit()
        for string in stringsNode.findall('string'):
            if 'id' not in string.attrib:
                log('Warning: found <string> tag with no "id" attribute')
                continue
            if string.text == None or string.text.strip() == '':
                log('Warning: empty <string> tag for id=%s' %
                    string.attrib['id'])
                continue
            self.strings[int(
                string.attrib['id'])] = string.text.strip().lower()
Exemple #14
0
	def getFont(self):
		'''Parse the current skin's Font.xml file for a list of font names by
		size'''
		log('Loading font set from current skin: ' + xbmc.getSkinDir())
		
		fontName = ''
		# Use letterHeight (reasoning: WIDTH may be elastic in the future)
		desiredSize = self.letterHeight * 1 / 2 # Decent ratio
		fallback = 'font'
		
		# Font.xml can be in any resolution folder, keep trying until we find
		# one. Use the first Font.xml we come across.
		skinDir = xbmc.translatePath("special://skin/")
		for item in os.listdir(skinDir):
			fontFile = os.path.join(skinDir, item, 'Font.xml')
			if not os.path.exists(fontFile):
				continue
			try:
				root = parse(fontFile).getroot()
			except:
				continue
			for set in root.findall('fontset'):
				# Now that we've found the file, use the Default fontset
				# (guaranteed to exist regardless of skin)
				if 'id' not in set.attrib or set.attrib['id'] != 'Default':
					continue
				# Add one so we don't lie when saying "smaller" (versus "smaller than or equal to")
				log('Font set loaded. Searching for a font smaller than %dpt' % (desiredSize + 1))
				# Index the discovered fonts into two categories
				fontsWithoutStyle = {}
				fontsWithStyle = {}
				for font in set.findall('font'):
					if font.find('size') == None or font.find('name') == None:
						continue
					size = int(font.find('size').text)
					# Skip fonts larger than the desired size
					if size > desiredSize:
						continue
					if not font.find('style'):
						fontsWithoutStyle[size] = font.find('name').text
					else:
						fontsWithStyle[size] = font.find('name').text
				# Categories generated. Prefer unstyled fonts
				if len(fontsWithoutStyle):
					max = sorted(fontsWithoutStyle.keys(), reverse=True)[0]
					log('Using unstyled font "%s" (%dpt)' % (fontsWithoutStyle[max], max))
					return fontsWithoutStyle[max]
				elif len(fontsWithStyle):
					max = sorted(fontsWithStyle.keys(), reverse=True)[0]
					log('Using styled font "%s" (%dpt)' % (fontsWithStyle[max], max))
					return fontsWithStyle[max]
				log('No suitable fonts found. Falling back to ' + fallback)
				return fallback
			log('Default font set not found. Falling back to ' + fallback)
			return fallback
		log('Font.xml not found. Falling back to ' + fallback)
		return fallback
Exemple #15
0
	def refresh(self):
		log('Stopping Qlock thread')
		self.qlockThread.stop()
		log('Qlock thread stopped? Sleeping')
Exemple #16
0
	def __init__(self, file):
		log('Using layout: ' + os.path.basename(file))
		try:
			root = ElementTree.parse(file).getroot()
		except:
			log('Error parsing layout file!')
			raise # Let the user see the error
		try:
			background = root.find('background')
			self.height = int(background.attrib['height'])
			self.width = int(background.attrib['width'])
		except:
			log('Error: <background> tag missing/incorrect!')
			sys.exit()
		
		self.matrix = []
		entities = [char.strip().upper() for char in background.text.split(',')]
		if (self.height * self.width > len(entities)):
			log('Error: Too many characters in background (expecting %d, found %d)' % \
				(self.height * self.width, len(entities)))
			sys.exit()
		elif (self.height * self.width < len(entities)):
			log('Error: Too few characters in background (expecting %d, found %d)' % \
				(self.height * self.width, len(entities)))
			sys.exit()
		for i in range(self.height):
			self.matrix.append(entities[i * self.width : (i + 1) * self.width])
		
		timesNode = root.find('times')
		if timesNode == None:
			log('Error: <times> node not found!')
			sys.exit()
		if 'use24' in timesNode.attrib and timesNode.attrib['use24'] == 'true':
			self.use24 = True
		else:
			self.use24 = False
		
		self.times = {}
		for time in timesNode.findall('time'):
			if 'id' not in time.attrib:
				log('Warning: found <time> tag with no "id" attribute')
				continue
			key = Time(time.attrib['id'])
			# If set, store the duration with the key
			if 'duration' in time.attrib:
				key.duration = Time(time.attrib['duration'])
			self.times[key] = time.text.lower()
		if len(self.times) == 0:
			log('Error: no <time> tags found!')
			sys.exit()
		
		self.strings = {}
		stringsNode = root.find('strings')
		if stringsNode == None:
			log('Error: <strings> node not found!')
			sys.exit()
		for string in stringsNode.findall('string'):
			if 'id' not in string.attrib:
				log('Warning: found <string> tag with no "id" attribute')
				continue
			if string.text == None or string.text.strip() == '':
				log('Warning: empty <string> tag for id=%s' % string.attrib['id'])
				continue
			self.strings[int(string.attrib['id'])] = string.text.strip().lower()