def _get_widget_path_at_line(self, lineno, root_lineno=0): '''To get widget path of widget at line ''' if self.text == '': return [] text = self.text # Remove all comments text = re.sub(r'#.+', '', text) lines = text.splitlines() line = lines[lineno] # Search for the line containing widget's name _lineno = lineno while line.find(':') != -1 and \ line.strip().find(':') != len(line.strip()) - 1: lineno -= 1 line = lines[lineno] path = [] child_count = 0 # From current line go above and # fill number of children above widget's rule while _lineno >= root_lineno and lines[_lineno].strip() != "" and \ get_indentation(lines[lineno]) != 0: _lineno = lineno - 1 diff_indent = get_indentation(lines[lineno]) - \ get_indentation(lines[_lineno]) while _lineno >= root_lineno and (lines[_lineno].strip() == '' or diff_indent <= 0): if lines[_lineno].strip() != '' and diff_indent == 0 and \ 'canvas' not in lines[_lineno] and \ (lines[_lineno].find(':') == -1 or lines[_lineno].find(':') == len(lines[_lineno].rstrip()) - 1): child_count += 1 _lineno -= 1 diff_indent = get_indentation(lines[lineno]) - \ get_indentation(lines[_lineno]) lineno = _lineno if _lineno > root_lineno: _lineno += 1 if 'canvas' not in lines[_lineno] and \ lines[_lineno].strip().find(':') == \ len(lines[_lineno].strip()) - 1: path.insert(0, child_count) child_count = 0 return path
def _get_widget_path_at_line(self, lineno, root_lineno=0): """To get widget path of widget at line """ if self.text == "": return [] text = self.text # Remove all comments text = re.sub(r"#.+", "", text) lines = text.splitlines() line = lines[lineno] # Search for the line containing widget's name _lineno = lineno while line.find(":") != -1 and line.strip().find(":") != len(line.strip()) - 1: lineno -= 1 line = lines[lineno] path = [] child_count = 0 # From current line go above and # fill number of children above widget's rule while _lineno >= root_lineno and lines[_lineno].strip() != "" and get_indentation(lines[lineno]) != 0: _lineno = lineno - 1 diff_indent = get_indentation(lines[lineno]) - get_indentation(lines[_lineno]) while _lineno >= root_lineno and (lines[_lineno].strip() == "" or diff_indent <= 0): if ( lines[_lineno].strip() != "" and diff_indent == 0 and "canvas" not in lines[_lineno] and (lines[_lineno].find(":") == -1 or lines[_lineno].find(":") == len(lines[_lineno].rstrip()) - 1) ): child_count += 1 _lineno -= 1 diff_indent = get_indentation(lines[lineno]) - get_indentation(lines[_lineno]) lineno = _lineno if _lineno > root_lineno: _lineno += 1 if "canvas" not in lines[_lineno] and lines[_lineno].strip().find(":") == len(lines[_lineno].strip()) - 1: path.insert(0, child_count) child_count = 0 return path
def _find_widget_place(self, path, lines, total_lines, lineno, indent=4): '''To find the line where widget is declared according to path ''' child_count = 0 path_index = 1 path_length = len(path) # From starting line go down to find the widget's rule according to path while lineno < total_lines and path_index < path_length: line = lines[lineno] _indent = get_indentation(line) colon_pos = line.find(':') if _indent == indent and line.strip() != '': if colon_pos != -1: line = line.rstrip() if colon_pos == len(line) - 1 and 'canvas' not in line: line = line[:colon_pos].lstrip() if child_count == path[path_index]: path_index += 1 indent = _indent + 4 child_count = 0 else: child_count += 1 else: child_count += 1 lineno += 1 return lineno - 1
def get_widget_text_pos_from_kv(self, widget, parent=None, path_to_widget=None): '''To get start and end pos of widget's rule in kv text :param path_to_widget: array with widget path :param parent: parent of widget :param widget: widget to find the kv text ''' if not path_to_widget: path_to_widget = self.get_widget_path(widget) path_to_widget.reverse() # Go to widget's rule's line and determines all its rule's # and it's child if any. Then delete them text = re.sub(r'#.+', '', self.text) lines = text.splitlines() total_lines = len(lines) root_lineno = 0 root_name = self.playground.root_name for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break widget_lineno = self._find_widget_place(path_to_widget, lines, total_lines, root_lineno + 1) widget_line = lines[widget_lineno] indent = len(widget_line) - len(widget_line.lstrip()) lineno = widget_lineno _indent = indent + 1 line = widget_line while line.strip() == '' or _indent > indent: lineno += 1 if lineno >= total_lines: break line = lines[lineno] _indent = len(line) - len(line.lstrip()) delete_until_line = lineno - 1 line = lines[delete_until_line] while line.strip() == '': delete_until_line -= 1 line = lines[delete_until_line] widget_line_pos = get_line_start_pos(self.text, widget_lineno) delete_until_line_pos = -1 if delete_until_line == total_lines - 1: delete_until_line_pos = len(self.text) else: delete_until_line_pos = get_line_end_pos(self.text, delete_until_line) self._reload = False return widget_line_pos, delete_until_line_pos
def get_class_str_from_text(self, class_name, _file_str, is_class=True): '''To return the full class rule of class_name from _file_str ''' _file_str += '\n' start_pos = -1 # Find the start position of class_name if is_class: start_pos = _file_str.find('<' + class_name + '>:') else: while True: start_pos = _file_str.find(class_name, start_pos + 1) if start_pos == 0 or not (_file_str[start_pos - 1].isalnum() and _file_str[start_pos - 1] != ''): break _line = 0 _line_pos = 0 _line_pos = _file_str.find('\n', _line_pos + 1) while _line_pos != -1 and _line_pos < start_pos: _line_pos = _file_str.find('\n', _line_pos + 1) _line += 1 # Find the end position of class_name, where indentation becomes 0 # or file ends _line += 1 lines = _file_str.splitlines() _total_lines = len(lines) hash_pos = 0 while hash_pos == 0 and _line < _total_lines: hash_pos = lines[_line].find('#') if hash_pos == 0: _line_pos += 1 + len(lines[_line]) _line += 1 while _line < _total_lines and (lines[_line].strip() == '' or get_indentation(lines[_line]) != 0): _line_pos = _file_str.find('\n', _line_pos + 1) _line += 1 hash_pos = 0 while hash_pos == 0 and _line < _total_lines: hash_pos = lines[_line].find('#') if hash_pos == 0: _line += 1 end_pos = _line_pos old_str = _file_str[start_pos: end_pos] return old_str
def add_custom_widget(self, py_path): '''This function is used to add a custom widget given path to its py file. ''' f = open(py_path, 'r') py_string = f.read() f.close() # Find path to kv. py file will have Builder.load_file('path/to/kv') _r = re.findall(r'Builder\.load_file\s*\(\s*.+\s*\)', py_string) if _r == []: raise ProjectLoaderException('Cannot find widget\'s kv file.') py_string = py_string.replace(_r[0], '') kv_path = _r[0][_r[0].find('(') + 1: _r[0].find(')')] py_string = py_string.replace(kv_path, '') kv_path = kv_path.replace("'", '').replace('"', '') f = open(kv_path, 'r') kv_string = f.read() f.close() # Remove all the 'app' lines for app_str in re.findall(r'.+app+.+', kv_string): kv_string = kv_string.replace( app_str, app_str[:get_indentation(app_str)] + '#' + app_str.lstrip()) Builder.load_string(kv_string) sys.path.insert(0, os.path.dirname(kv_path)) _to_check = [] # Get all the class_rules for class_str in re.findall(r'<+([\w_]+)>', kv_string): if re.search(r'\bclass\s+%s+.+:' % class_str, py_string): module = imp.new_module('CustomWidget') exec py_string in module.__dict__ sys.modules['AppModule'] = module class_rule = CustomWidgetRule(class_str, kv_path, py_path) class_rule.file = py_path class_rule.module = module self.custom_widgets.append(class_rule)
def shift_widget(self, widget, from_index): '''This function will shift widget's kv str from one position to another. :param from_index: original index of widget before moving :param widget: shifted widget ''' self._reload = False path = self.get_widget_path(widget) path.reverse() # copies the original path prev_path = list(path) # get the path before shifting the widget prev_path[-1] = len(widget.parent.children) - from_index - 1 start_pos, end_pos = self.get_widget_text_pos_from_kv( widget, widget.parent, path_to_widget=prev_path) widget_text = self.text[start_pos:end_pos] if widget.parent.children.index(widget) == 0: self.text = self.text[:start_pos] + self.text[end_pos:] self.add_widget_to_parent(widget, widget.parent, kv_str=widget_text) else: self.text = self.text[:start_pos] + self.text[end_pos:] text = re.sub(r'#.+', '', self.text) lines = text.splitlines() total_lines = len(lines) root_lineno = 0 root_name = self.playground.root_name for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break next_widget_path = path lineno = self._find_widget_place(next_widget_path, lines, total_lines, root_lineno + 1) self.cursor = (0, lineno) self.insert_text(widget_text + '\n')
def get_root_str(self, kv_str=''): '''This function will get the root widgets rule from either kv_str or if it is empty string then from the kv file of root widget ''' if kv_str == '': f = open(self.root_rule.kv_file, 'r') kv_str = f.read() f.close() # Find the start position of root_rule start_pos = kv_str.find(self.root_rule.name) if start_pos == -1: raise ProjectLoaderException( 'Cannot find root rule in its file') # Get line for start_pos _line = 0 _line_pos = 0 _line_pos = kv_str.find('\n', _line_pos + 1) while _line_pos != -1 and _line_pos < start_pos: _line_pos = kv_str.find('\n', _line_pos + 1) _line += 1 # Find the end position of root_rule, where indentation becomes 0 # or file ends _line += 1 lines = kv_str.splitlines() _total_lines = len(lines) while _line < _total_lines and (lines[_line].strip() == '' or get_indentation(lines[_line]) != 0): _line_pos = kv_str.find('\n', _line_pos + 1) _line += 1 end_pos = _line_pos root_old_str = kv_str[start_pos: end_pos] for _rule in self.class_rules: if _rule.name == self.root_rule.name: root_old_str = "<" + root_old_str return root_old_str
def shift_widget(self, widget, from_index): '''This function will shift widget's kv str from one position to another. ''' self._reload = False path = self._get_widget_path(widget) path.reverse() prev_path = [x for x in path] prev_path[-1] = len(widget.parent.children) - from_index - 1 start_pos, end_pos = self.get_widget_text_pos_from_kv(widget, widget.parent, path_to_widget= prev_path) widget_text = self.text[start_pos:end_pos] if widget.parent.children.index(widget) == 0: self.text = self.text[:start_pos] + self.text[end_pos:] self.add_widget_to_parent(widget, widget.parent, kv_str=widget_text) else: self.text = self.text[:start_pos] + self.text[end_pos:] text = re.sub(r'#.+', '', self.text) lines = text.splitlines() total_lines = len(lines) root_lineno = 0 root_name = self.project_loader.root_rule.name for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break next_widget_path = path lineno = self._find_widget_place(next_widget_path, lines, total_lines, root_lineno + 1) self.cursor = (0, lineno) self.insert_text(widget_text+'\n')
def shift_widget(self, widget, from_index): '''This function will shift widget's kv str from one position to another. ''' self._reload = False path = self._get_widget_path(widget) path.reverse() prev_path = [x for x in path] prev_path[-1] = len(widget.parent.children) - from_index - 1 start_pos, end_pos = self.get_widget_text_pos_from_kv( widget, widget.parent, path_to_widget=prev_path) widget_text = self.text[start_pos:end_pos] if widget.parent.children.index(widget) == 0: self.text = self.text[:start_pos] + self.text[end_pos:] self.add_widget_to_parent(widget, widget.parent, kv_str=widget_text) else: self.text = self.text[:start_pos] + self.text[end_pos:] text = re.sub(r'#.+', '', self.text) lines = text.splitlines() total_lines = len(lines) root_lineno = 0 root_name = self.project_loader.root_rule.name for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break next_widget_path = path lineno = self._find_widget_place(next_widget_path, lines, total_lines, root_lineno + 1) self.cursor = (0, lineno) self.insert_text(widget_text + '\n')
def add_widget_to_parent(self, widget, target, kv_str=''): '''This function is called when widget is added to target. It will search for line where parent is defined in text and will add widget there. ''' text = re.sub(r'#.+', '', self.text) lines = text.splitlines() total_lines = len(lines) if total_lines == 0: return self._reload = False # If target is not none then widget is not root widget if target: path_to_widget = self.get_widget_path(target) path_to_widget.reverse() root_lineno = 0 root_name = self.playground.root_name for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break parent_lineno = self._find_widget_place(path_to_widget, lines, total_lines, root_lineno + 1) if parent_lineno >= total_lines: return # Get text of parents line parent_line = lines[parent_lineno] if not parent_line.strip(): return insert_after_line = -1 if parent_line.find(':') == -1: # If parent_line doesn't contain ':' then insert it # Also insert widget's rule after its properties insert_after_line = parent_lineno _line = 0 _line_pos = -1 _line_pos = self.text.find('\n', _line_pos + 1) while _line <= insert_after_line: _line_pos = self.text.find('\n', _line_pos + 1) _line += 1 self.text = self.text[:_line_pos] + ':' + self.text[_line_pos:] indent = len(parent_line) - len(parent_line.lstrip()) else: # If ':' in parent_line then, # find a place to insert widget's rule indent = len(parent_line) - len(parent_line.lstrip()) lineno = parent_lineno _indent = indent + 1 line = parent_line while (line.strip() == '' or _indent > indent): lineno += 1 if lineno >= total_lines: break line = lines[lineno] _indent = len(line) - len(line.lstrip()) insert_after_line = lineno - 1 line = lines[insert_after_line] while line.strip() == '': insert_after_line -= 1 line = lines[insert_after_line] to_insert = '' # counts indentation in the beginning of the string extra_indent = len(kv_str) - len(kv_str.lstrip()) if kv_str == '': to_insert = type(widget).__name__ + ':' else: to_insert = kv_str if insert_after_line == total_lines - 1: # if inserting at the last line _line_pos = len(self.text) - 1 indent = get_indent_str(indent + 4 - extra_indent) to_add = '' for line in to_insert.splitlines(): to_add += '\n' + indent + line self.text = self.text[:_line_pos + 1] + to_add else: # inserting somewhere else insert_after_line -= 1 _line = 0 _line_pos = -1 _line_pos = self.text.find('\n', _line_pos + 1) while _line <= insert_after_line: _line_pos = self.text.find('\n', _line_pos + 1) _line += 1 self.text = self.text[:_line_pos] + '\n' + \ get_indent_str(indent + 4) + to_insert + \ self.text[_line_pos:] else: # widget is a root widget parent_lineno = 0 self.cursor = (0, 0) type_name = type(widget).__name__ is_class = False app_widgets = get_current_project().app_widgets for rule_name in app_widgets: if rule_name == type_name: is_class = True break if not is_class: self.insert_text(type_name + ':\n') self.playground.load_widget(type_name)
def _load_project(self, kv_path): '''Pivate function to load any project given by kv_path ''' if os.path.isdir(kv_path): self.proj_dir = kv_path else: self.proj_dir = os.path.dirname(kv_path) parent_proj_dir = os.path.dirname(self.proj_dir) sys.path.insert(0, parent_proj_dir) self.class_rules = [] all_files_loaded = True _file = None for _file in os.listdir(self.proj_dir): # Load each kv file in the directory _file = os.path.join(self.proj_dir, _file) if _file[_file.rfind('.'):] != '.kv': continue self.kv_file_list.append(_file) f = open(_file, 'r') kv_string = f.read() f.close() # Remove all the 'app' lines for app_str in re.findall(r'.+app+.+', kv_string): kv_string = kv_string.replace( app_str, app_str[:get_indentation(app_str)] + '#' + app_str.lstrip()) # Get all the class_rules for class_str in re.findall(r'<+([\w_]+)>', kv_string): class_rule = ClassRule(class_str) class_rule.kv_file = _file self.class_rules.append(class_rule) try: root_name = re.findall(r'^([\w\d_]+)\:', kv_string, re.MULTILINE) if root_name != []: # It will occur when there is a root rule and it can't # be loaded by Builder because the its file # has been imported root_name = root_name[0] if not hasattr(Factory, root_name): match = re.search(r'^([\w\d_]+)\:', kv_string, re.MULTILINE) kv_string = kv_string[:match.start()] + \ '<' + root_name + '>:' + kv_string[match.end():] self.root_rule = RootRule(root_name, None) self.root_rule.kv_file = _file self._root_rule = self.root_rule self._is_root_already_in_factory = False else: self._is_root_already_in_factory = True else: self._is_root_already_in_factory = False root_rule = Builder.load_string(re.sub(r'\s+on_\w+:\w+', '', kv_string)) if root_rule: self.root_rule = RootRule(root_rule.__class__.__name__, root_rule) self.root_rule.kv_file = _file self._root_rule = self.root_rule except Exception as e: all_files_loaded = False if not all_files_loaded: raise ProjectLoaderException('Cannot load file "%s"' % (_file)) if os.path.exists(os.path.join(self.proj_dir, KV_PROJ_FILE_NAME)): projdir_mtime = os.path.getmtime(self.proj_dir) f = open(os.path.join(self.proj_dir, KV_PROJ_FILE_NAME), 'r') proj_str = f.read() f.close() _file_is_valid = True # Checking if the file is valid if proj_str == '' or\ proj_str.count('<files>') != proj_str.count('</files>') or\ proj_str.count('<file>') != proj_str.count('</file>') or\ proj_str.count('<class>') != proj_str.count('</class>'): _file_is_valid = False if _file_is_valid: projdir_time = proj_str[ proj_str.find('<time>') + len('<time>'): proj_str.find('</time>')] projdir_time = float(projdir_time.strip()) if _file_is_valid and projdir_mtime <= projdir_time: # Project Directory folder hasn't been modified, # file list will remain same self.file_list = [] un_modified_files = [] start_pos = proj_str.find('<files>') end_pos = proj_str.find('</files>') if start_pos != -1 and end_pos != -1: start_pos = proj_str.find('<file>', start_pos) end_pos1 = proj_str.find('</file>', start_pos) while start_pos < end_pos and start_pos != -1: _file = proj_str[ start_pos + len('<file>'):end_pos1].strip() self.file_list.append(_file) if os.path.getmtime(_file) <= projdir_time: un_modified_files.append(_file) start_pos = proj_str.find('<file>', end_pos1) end_pos1 = proj_str.find('</file>', start_pos) for _file in self.file_list: _dir = os.path.dirname(_file) if _dir not in sys.path: sys.path.insert(0, _dir) # Reload information for app start_pos = proj_str.find('<app>') end_pos = proj_str.find('</app>') if start_pos != -1 and end_pos != -1: self._app_class = proj_str[ proj_str.find('<class>', start_pos) + len('<class>'): proj_str.find('</class>', start_pos)].strip() self._app_file = proj_str[ proj_str.find('<file>', start_pos) + len('<file>'): proj_str.find('</file>', start_pos)].strip() f = open(self._app_file, 'r') self._app_module = self._import_module(f.read(), self._app_file) f.close() # Reload information for the files which haven't been modified start_pos = proj_str.find('<classes>') end_pos = proj_str.find('</classes>') if start_pos != -1 and end_pos != -1: while start_pos < end_pos and start_pos != -1: start_pos = proj_str.find('<class>', start_pos) +\ len('<class>') end_pos1 = proj_str.find('</class>', start_pos) _file = proj_str[ proj_str.find('<file>', start_pos) + len('<file>'): proj_str.find('</file>', start_pos)].strip() if _file in un_modified_files: # If _file is un modified then assign it to # class rule with _name _name = proj_str[ proj_str.find('<name>', start_pos) + len('<name>'): proj_str.find('</name>', start_pos)].strip() for _rule in self.class_rules: if _name == _rule.name: _rule.file = _file f = open(_file, 'r') _rule.module = self._import_module( f.read(), _file, _fromlist=[_name]) f.close() start_pos = proj_str.find('<class>', start_pos) end_pos1 = proj_str.find('</class>', start_pos) if self.file_list == []: self.file_list = self._get_file_list(self.proj_dir) # Get all files corresponding to each class self._get_class_files() # If root widget is not created but root class is known # then create widget if self.root_rule and not self.root_rule.widget and \ self.root_rule.name: self.root_rule.widget = self.get_widget_of_class( self.root_rule.name) self.load_proj_config()
def save_project(self, proj_dir=''): '''To save project to proj_dir. If proj_dir is not empty string then project is saved to a new directory other than its current directory and otherwise it is saved to the current directory. ''' # To stop ProjectWatcher from emitting event when project is saved self.proj_watcher.allow_event_dispatch = False proj_dir_changed = False if self.new_project: # Create dir and copy new_proj.kv and new_proj.py to new directory if not os.path.exists(proj_dir): os.mkdir(proj_dir) kivy_designer_dir = get_kivy_designer_dir() kivy_designer_new_proj_dir = os.path.join(kivy_designer_dir, "new_proj") for _file in os.listdir(kivy_designer_new_proj_dir): old_file = os.path.join(kivy_designer_new_proj_dir, _file) new_file = os.path.join(proj_dir, _file) if os.path.isdir(old_file): shutil.copytree(old_file, new_file) else: shutil.copy(old_file, new_file) self.file_list = self._get_file_list(proj_dir) new_kv_file = os.path.join(proj_dir, "main.kv") new_py_file = os.path.join(proj_dir, "main.py") self.proj_dir = proj_dir if self.root_rule: self.root_rule.kv_file = new_kv_file self.root_rule.py_file = new_py_file if self.class_rules: self.class_rules[0].py_file = new_py_file self.class_rules[0].kv_file = new_kv_file self.new_project = False else: if proj_dir != '' and proj_dir != self.proj_dir: proj_dir_changed = True # Remove previous project directories from sys.path for _dir in self._dir_list: try: sys.path.remove(_dir) except: pass # if proj_dir and self.proj_dir differs then user wants to save # an already opened project to somewhere else # Copy all the files if not os.path.exists(proj_dir): os.mkdir(proj_dir) for _file in os.listdir(self.proj_dir): old_file = os.path.join(self.proj_dir, _file) new_file = os.path.join(proj_dir, _file) if os.path.isdir(old_file): shutil.copytree(old_file, new_file) else: shutil.copy(old_file, new_file) self.file_list = self._get_file_list(proj_dir) # Change the path of all files in the class rules, # root rule and app relative_path = self._app_file[ self._app_file.find(self.proj_dir):] self._app_file = os.path.join(proj_dir, relative_path) f = open(self._app_file, 'r') s = f.read() f.close() self._import_module(s, self._app_file, _fromlist=[self._app_class]) for _rule in self.class_rules: relative_path = _rule.kv_file[ _rule.kv_file.find(self.proj_dir):] _rule.kv_file = os.path.join(proj_dir, relative_path) relative_path = _rule.file[_rule.file.find(self.proj_dir):] _rule.file = os.path.join(proj_dir, relative_path) f = open(_rule.file, 'r') s = f.read() f.close() self._import_module(s, _rule.file, _fromlist=[_rule.name]) relative_path = self.root_rule.kv_file[ self.root_rule.kv_file.find(self.proj_dir):] self.root_rule.kv_file = os.path.join(proj_dir, relative_path) relative_path = self.root_rule.file[ self.root_rule.file.find(self.proj_dir):] self.root_rule.file = os.path.join(proj_dir, relative_path) self.proj_dir = proj_dir # For custom widgets copy py and kv file to project directory for widget in self.custom_widgets: custom_kv = os.path.join(self.proj_dir, os.path.basename(widget.kv_file)) if not os.path.exists(custom_kv): shutil.copy(widget.kv_file, custom_kv) custom_py = os.path.join(self.proj_dir, os.path.basename(widget.py_file)) if not os.path.exists(custom_py): shutil.copy(widget.py_file, custom_py) # Saving all opened py files and also reimport them for _code_input in self.tab_pannel.list_py_code_inputs: path = os.path.join(self.proj_dir, _code_input.rel_file_path) f = open(path, 'w') f.write(_code_input.text) f.close() _from_list = [] for rule in self.class_rules: if rule.file == path: _from_list.append(rule.file) if not self.is_root_a_class_rule(): if self.root_rule.file == path: _from_list.append(self.root_rule.name) # Ignore all types that are not .py if path.endswith(".py"): self._import_module(_code_input.text, path, _fromlist=_from_list) # Save all class rules text = self.kv_code_input.text for _rule in self.class_rules: # Get the kv text from KVLangArea and write it to class rule's file f = open(_rule.kv_file, 'r') _file_str = f.read() f.close() old_str = self.get_class_str_from_text(_rule.name, _file_str) new_str = self.get_class_str_from_text(_rule.name, text) f = open(_rule.kv_file, 'w') _file_str = _file_str.replace(old_str, new_str) f.write(_file_str) f.close() # If root widget is not changed if self._root_rule.name == self.root_rule.name: # Save root widget's rule is_root_class = False for _rule in self.class_rules: if _rule.name == self.root_rule.name: is_root_class = True break if not is_root_class: f = open(self.root_rule.kv_file, 'r') _file_str = f.read() f.close() old_str = self.get_class_str_from_text(self.root_rule.name, _file_str, is_class=False) new_str = self.get_class_str_from_text(self.root_rule.name, text, is_class=False) f = open(self.root_rule.kv_file, 'w') _file_str = _file_str.replace(old_str, new_str) f.write(_file_str) f.close() else: # If root widget is changed # Root Widget changes, there can be these cases: root_name = self.root_rule.name f = open(self._app_file, 'r') file_str = f.read() f.close() self._root_rule = self.root_rule if self.is_root_a_class_rule() and self._app_file: # Root Widget's class rule is a custom class # and its rule is class rule. So, it already have been saved # the string of App's build() function will be changed to # return new root widget's class if self._app_class != 'runTouchApp': s = re.search(r'class\s+%s.+:' % self._app_class, file_str) if s: build_searchiter = None for searchiter in re.finditer( r'[ \ \t]+def\s+build\s*\(\s*self.+\s*:', file_str): if searchiter.start() > s.start(): build_searchiter = searchiter break if build_searchiter: indent = get_indentation(build_searchiter.group(0)) file_str = file_str[:build_searchiter.end()] +\ '\n' + get_indent_str(2 * indent) + "return " +\ root_name + "()\n" + \ file_str[build_searchiter.end():] else: file_str = file_str[:s.end()] + \ "\n def build(self):\n return " + \ root_name + '()\n' + file_str[s.end():] else: file_str = re.sub(r'runTouchApp\s*\(.+\)', 'runTouchApp(' + root_name + '())', file_str) f = open(self._app_file, 'w') f.write(file_str) f.close() else: # Root Widget's rule is not a custom class # and its rule is root rule # Its kv_file should be of App's class name # and App's build() function should be cleared if not self.root_rule.kv_file: s = self._app_class.replace('App', '').lower() root_file = None for _file in self.kv_file_list: if os.path.basename(_file).find(s) == 0: self.root_rule.kv_file = _file break f = open(self.root_rule.kv_file, 'r') _file_str = f.read() f.close() new_str = self.get_class_str_from_text(self.root_rule.name, text, False) f = open(self.root_rule.kv_file, 'a') f.write(new_str) f.close() if self._app_class != 'runTouchApp': s = re.search(r'class\s+%s.+:' % self._app_class, file_str) if s: build_searchiter = None for searchiter in re.finditer( r'[ \ \t]+def\s+build\s*\(\s*self.+\s*:', file_str): if searchiter.start() > s.start(): build_searchiter = searchiter break if build_searchiter: lines = file_str.splitlines() total_lines = len(lines) indent = get_indentation(build_searchiter.group(0)) _line = 0 _line_pos = -1 _line_pos = file_str.find('\n', _line_pos + 1) while _line_pos <= build_searchiter.start(): _line_pos = file_str.find('\n', _line_pos + 1) _line += 1 _line += 1 while _line < total_lines: if lines[_line].strip() != '' and\ get_indentation(lines[_line]) <= \ indent: break _line += 1 _line -= 1 end = get_line_start_pos(file_str, _line) start = build_searchiter.start() file_str = file_str.replace(file_str[start:end], ' pass') f = open(self._app_file, 'w') f.write(file_str) f.close() # Allow Project Watcher to emit events Clock.schedule_once(self._allow_proj_watcher_dispatch, 1)
def set_property_value(self, widget, prop, value, proptype): '''To find and change the value of property of widget rule in text ''' # Do not add property if value is empty and # property is not a string property self._reload = False if not isinstance(widget.properties()[prop], StringProperty) and\ value == '': return path_to_widget = self.get_widget_path(widget) path_to_widget.reverse() # Go to the line where widget is declared lines = re.sub(r'#.+', '', self.text.rstrip()).splitlines() total_lines = len(lines) root_name = self.playground.root_name total_lines = len(lines) root_lineno = 0 for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break widget_lineno = self._find_widget_place(path_to_widget, lines, total_lines, root_lineno + 1) widget_line = lines[widget_lineno] if not widget_line.strip(): return indent = get_indentation(widget_line) prop_found = False if ':' not in widget_line: # If cannot find ':' then insert it self.cursor = (len(lines[widget_lineno]), widget_lineno) lines[widget_lineno] += ':' self.insert_text(':') else: # Else find if property has already been declared with a value lineno = widget_lineno + 1 # But if widget line is the last line in the text if lineno < total_lines: line = lines[lineno] _indent = get_indentation(line) colon_pos = -1 while lineno < total_lines and (line.strip() == '' or _indent > indent): line = lines[lineno] _indent = get_indentation(line) if line.strip() != '': colon_pos = line.find(':') if colon_pos == -1: break if colon_pos == len(line.rstrip()) - 1: break if prop == line[:colon_pos].strip(): prop_found = True break lineno += 1 if prop_found: # if property found then change its value _pos_prop_value = get_line_start_pos(self.text, lineno) + \ colon_pos + 2 if lineno == total_lines - 1: _line_end_pos = len(self.text) else: _line_end_pos = get_line_end_pos(self.text, lineno) if proptype == 'StringProperty' or \ (proptype == 'OptionProperty' and not isinstance(value, list)): value = "'{}'".format(value.replace("'", "\\'")) self.text = self.text[:_pos_prop_value] + ' ' + str(value) + \ self.text[_line_end_pos:] self.cursor = (0, lineno) else: # if not found then add property after the widgets line _line_start_pos = get_line_start_pos(self.text, widget_lineno) _line_end_pos = get_line_end_pos(self.text, widget_lineno) if proptype == 'StringProperty' or \ (proptype == 'OptionProperty' and not isinstance(value, list)): value = "'{}'".format(value.replace("'", "\\'")) indent_str = '\n' for i in range(indent + 4): indent_str += ' ' self.cursor = (len(lines[widget_lineno]), widget_lineno) self.insert_text(indent_str + prop + ': ' + str(value))
def get_property_value(self, widget, prop): self._reload = False if prop[:3] != 'on_' and \ not isinstance(widget.properties()[prop], StringProperty) and\ value == '': return path_to_widget = self.get_widget_path(widget) path_to_widget.reverse() # Go to the line where widget is declared lines = re.sub(r'#.+', '', self.text).splitlines() total_lines = len(lines) root_name = self.playground.root_name total_lines = len(lines) root_lineno = 0 for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break widget_lineno = self._find_widget_place(path_to_widget, lines, total_lines, root_lineno + 1) widget_line = lines[widget_lineno] indent = get_indentation(widget_line) prop_found = False # Else find if property has already been declared with a value lineno = widget_lineno + 1 # But if widget line is the last line in the text if lineno < total_lines: line = lines[lineno] _indent = get_indentation(line) colon_pos = -1 while lineno < total_lines and (line.strip() == '' or _indent > indent): line = lines[lineno] _indent = get_indentation(line) if line.strip() != '': colon_pos = line.find(':') if colon_pos == -1: break if colon_pos == len(line.rstrip()) - 1: break if prop == line[:colon_pos].strip(): prop_found = True break lineno += 1 if prop_found: # if property found then change its value _pos_prop_value = get_line_start_pos(self.text, lineno) + \ colon_pos + 2 if lineno == total_lines - 1: _line_end_pos = len(self.text) else: _line_end_pos = get_line_end_pos(self.text, lineno) return self.text[_pos_prop_value:_line_end_pos] return ''
def set_event_handler(self, widget, prop, value): self._reload = False path_to_widget = self.get_widget_path(widget) path_to_widget.reverse() # Go to the line where widget is declared lines = re.sub(r'#.+', '', self.text).splitlines() total_lines = len(lines) root_name = self.playground.root_name total_lines = len(lines) root_lineno = 0 for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break widget_lineno = self._find_widget_place(path_to_widget, lines, total_lines, root_lineno + 1) widget_line = lines[widget_lineno] indent = get_indentation(widget_line) prop_found = False if not widget_line.strip(): return if ':' not in widget_line: # If cannot find ':' then insert it self.cursor = (len(lines[widget_lineno]), widget_lineno) lines[widget_lineno] += ':' self.insert_text(':') else: # Else find if property has already been declared with a value lineno = widget_lineno + 1 # But if widget line is the last line in the text if lineno < total_lines: line = lines[lineno] _indent = get_indentation(line) colon_pos = -1 while lineno < total_lines and (line.strip() == '' or _indent > indent): line = lines[lineno] _indent = get_indentation(line) if line.strip() != '': colon_pos = line.find(':') if colon_pos == -1: break if colon_pos == len(line.rstrip()) - 1: break if prop == line[:colon_pos].strip(): prop_found = True break lineno += 1 if prop_found: if lineno == total_lines - 1: _line_end_pos = len(self.text) else: _line_end_pos = get_line_end_pos(self.text, lineno) if value != '': # if property found then change its value _pos_prop_value = get_line_start_pos(self.text, lineno) + \ colon_pos + 2 self.text = self.text[:_pos_prop_value] + ' ' + value + \ self.text[_line_end_pos:] self.cursor = (0, lineno) else: _line_start_pos = get_line_start_pos(self.text, widget_lineno) self.text = \ self.text[:get_line_start_pos(self.text, lineno)] + \ self.text[_line_end_pos:] elif value != '': # if not found then add property after the widgets line _line_end_pos = get_line_end_pos(self.text, widget_lineno) indent_str = '\n' for i in range(indent + 4): indent_str += ' ' self.cursor = (len(lines[widget_lineno]), widget_lineno) self.insert_text(indent_str + prop + ': ' + str(value))
def add_widget_to_parent(self, widget, target, kv_str=""): """This function is called when widget is added to target. It will search for line where parent is defined in text and will add widget there. """ text = re.sub(r"#.+", "", self.text) lines = text.splitlines() total_lines = len(lines) if total_lines == 0: return self._reload = False # If target is not none then widget is not root widget if target: path_to_widget = self._get_widget_path(target) path_to_widget.reverse() root_lineno = 0 root_name = self.playground.root_name for lineno, line in enumerate(lines): pos = line.find(root_name) if pos != -1 and get_indentation(line) == 0: root_lineno = lineno break parent_lineno = self._find_widget_place(path_to_widget, lines, total_lines, root_lineno + 1) if parent_lineno >= total_lines: return # Get text of parents line parent_line = lines[parent_lineno] if not parent_line.strip(): return insert_after_line = -1 if parent_line.find(":") == -1: # If parent_line doesn't contain ':' then insert it # Also insert widget's rule after its properties insert_after_line = parent_lineno _line = 0 _line_pos = -1 _line_pos = self.text.find("\n", _line_pos + 1) while _line <= insert_after_line: _line_pos = self.text.find("\n", _line_pos + 1) _line += 1 self.text = self.text[:_line_pos] + ":" + self.text[_line_pos:] indent = len(parent_line) - len(parent_line.lstrip()) else: # If ':' in parent_line then, # find a place to insert widget's rule indent = len(parent_line) - len(parent_line.lstrip()) lineno = parent_lineno _indent = indent + 1 line = parent_line while line.strip() == "" or _indent > indent: lineno += 1 if lineno >= total_lines: break line = lines[lineno] _indent = len(line) - len(line.lstrip()) insert_after_line = lineno - 1 line = lines[insert_after_line] while line.strip() == "": insert_after_line -= 1 line = lines[insert_after_line] to_insert = "" if kv_str == "": to_insert = type(widget).__name__ + ":" else: to_insert = kv_str.strip() if insert_after_line == total_lines - 1: # if inserting at the last line _line_pos = len(self.text) - 1 self.text = self.text[: _line_pos + 1] + "\n" + get_indent_str(indent + 4) + to_insert else: # inserting somewhere else insert_after_line -= 1 _line = 0 _line_pos = -1 _line_pos = self.text.find("\n", _line_pos + 1) while _line <= insert_after_line: _line_pos = self.text.find("\n", _line_pos + 1) _line += 1 self.text = ( self.text[:_line_pos] + "\n" + get_indent_str(indent + 4) + to_insert + self.text[_line_pos:] ) else: # widget is a root widget parent_lineno = 0 self.cursor = (0, 0) type_name = type(widget).__name__ is_class = False app_widgets = get_current_project().app_widgets for rule_name in app_widgets: if rule_name == type_name: is_class = True break if not is_class: self.insert_text(type_name + ":\n") self.playground.load_widget(type_name)