Это не возможно с маркировкой WinForms как есть. Маркировка должна иметь точно один шрифт точно с одним размером и одной поверхностью. У Вас есть несколько опций:
Редактировать : благодаря уточнению и изменению в спецификации я отредактировал свой код, все еще используя явный класс Node
в качестве промежуточного шага для ясности - - логика состоит в том, чтобы превратить список строк в список узлов, затем превратить этот список узлов в дерево (с помощью соответствующего атрибута отступа), затем распечатать это дерево в удобочитаемой форме (это просто «отладка- help "шаг, чтобы проверить, что дерево хорошо построено и, конечно, может быть закомментировано в окончательной версии скрипта, который, как и конечно, будет брать строки из файла, а не жестко закодировать их для отладки! -), наконец, создайте желаемую структуру Python и распечатайте ее. Вот код, и как мы После этого мы увидим, что результат почти , как указывает OP, с одним исключением - но сначала код:
import sys
class Node(object):
def __init__(self, title, indent):
self.title = title
self.indent = indent
self.children = []
self.notes = []
self.parent = None
def __repr__(self):
return 'Node(%s, %s, %r, %s)' % (
self.indent, self.parent, self.title, self.notes)
def aspython(self):
result = dict(title=self.title, children=topython(self.children))
if self.notes:
result['notes'] = self.notes
return result
def print_tree(node):
print ' ' * node.indent, node.title
for subnode in node.children:
print_tree(subnode)
for note in node.notes:
print ' ' * node.indent, 'Note:', note
def topython(nodelist):
return [node.aspython() for node in nodelist]
def lines_to_tree(lines):
nodes = []
for line in lines:
indent = len(line) - len(line.lstrip())
marker, body = line.strip().split(None, 1)
if marker == '*':
nodes.append(Node(body, indent))
elif marker == '-':
nodes[-1].notes.append(body)
else:
print>>sys.stderr, "Invalid marker %r" % marker
tree = Node('', -1)
curr = tree
for node in nodes:
while node.indent <= curr.indent:
curr = curr.parent
node.parent = curr
curr.children.append(node)
curr = node
return tree
data = """\
* 1
* 1.1
* 1.2
- Note for 1.2
* 2
* 3
- Note for root
""".splitlines()
def main():
tree = lines_to_tree(data)
print_tree(tree)
print
alist = topython(tree.children)
print alist
if __name__ == '__main__':
main()
При запуске выдает:
1
1.1
1.2
Note: 1.2
2
3
Note: 3
[{'children': [{'children': [], 'title': '1.1'}, {'notes': ['Note for 1.2'], 'children': [], 'title': '1.2'}], 'title': '1'}, {'children': [], 'title': '2'}, {'notes': ['Note for root'], 'children': [], 'title': '3'}]
Помимо порядка ключей (который является несущественны и не гарантируются в dict, конечно), это почти по запросу - за исключением того, что здесь все примечания отображаются как записи dict с ключом notes
и значение, представляющее собой список строк (но запись примечаний опускается, если список был бы пуст, примерно так, как это сделано в примере в вопросе).
В текущей версии вопроса, как представлять примечания немного неясно; одна заметка отображается как отдельная строка, другие - как записи, значение которых является строкой (вместо списка строк, который я использую). Непонятно что ' Предполагается, что примечание должно отображаться как отдельная строка в одном случае и как запись в формате dict во всех остальных, поэтому эта схема, которую я использую, является более регулярной; и если примечание (если оно есть) представляет собой одну строку, а не список, будет ли это означать ошибку, если для узла появляется более одной примечания? В последнем отношении эта схема, которую я использую, является более общей (позволяет узлу иметь любое количество заметок от 0 и выше, а не только 0 или 1, как очевидно подразумевается в вопросе)
Написав так много кода ( ответ перед редактированием был примерно таким же длинным и помог уточнить и изменить спецификации), чтобы предоставить (я надеюсь) 99% желаемого решения, я надеюсь, что это удовлетворит исходный плакат, поскольку последние несколько настроек кода и / или спецификаций для ему должно быть легко сделать так, чтобы они соответствовали друг другу!
и если примечание (если оно есть) представляет собой одну строку, а не список, будет ли это означать ошибку, если для узла появляется более одной примечания? В последнем отношении эта схема, которую я использую, является более общей (позволяет узлу иметь любое количество заметок от 0 и выше, а не только 0 или 1, как очевидно подразумевается в вопросе).Написав так много кода ( ответ перед редактированием был примерно таким же длинным и помог уточнить и изменить спецификации), чтобы предоставить (я надеюсь) 99% желаемого решения, я надеюсь, что это удовлетворит исходный плакат, поскольку последние несколько настроек кода и / или спецификаций для ему должно быть легко сделать так, чтобы они соответствовали друг другу!
и если примечание (если оно есть) представляет собой одну строку, а не список, будет ли это означать ошибку, если для узла появляется более одной примечания? В последнем отношении эта схема, которую я использую, является более общей (позволяет узлу иметь любое количество заметок от 0 и выше, а не только 0 или 1, как очевидно подразумевается в вопросе).Написав так много кода ( ответ перед редактированием был примерно таким же длинным и помог уточнить и изменить спецификации), чтобы предоставить (я надеюсь) 99% желаемого решения, я надеюсь, что это удовлетворит исходный плакат, поскольку последние несколько настроек кода и / или спецификаций для ему должно быть легко сделать так, чтобы они соответствовали друг другу!
Стеки - действительно полезная структура данных при синтаксическом анализе деревьев. Вы просто всегда сохраняете путь от последнего добавленного узла до корня в стеке, чтобы вы могли найти правильного родителя по длине отступа. Что-то вроде этого должно работать для анализа вашего последнего примера:
import re
line_tokens = re.compile('( *)(\\*|-) (.*)')
def parse_tree(data):
stack = [{'title': 'Root node', 'children': []}]
for line in data.split("\n"):
indent, symbol, content = line_tokens.match(line).groups()
while len(indent) + 1 < len(stack):
stack.pop() # Remove everything up to current parent
if symbol == '-':
stack[-1].setdefault('notes', []).append(content)
elif symbol == '*':
node = {'title': content, 'children': []}
stack[-1]['children'].append(node)
stack.append(node) # Add as the current deepest node
return stack[0]
Поскольку вы имеете дело со схемой, вы можете упростить задачу, используя стек. По сути, вы хотите создать стек, в котором есть dict
, соответствующие глубине контура. Когда вы анализируете новую строку и глубина контура увеличивается, вы помещаете новый dict
в стек, на который ссылался предыдущий dict
в верхней части стека. Когда вы разбираете строку с меньшей глубиной, вы выталкиваете стек, чтобы вернуться к родителю. И когда вы встречаетесь со строкой той же глубины, вы добавляете ее к dict
наверху стека.
Синтаксис, который вы используете, очень похож на Yaml. У него есть некоторые отличия, но его довольно легко изучить - его основное внимание уделяется тому, чтобы человек читал (и писал).
Взгляните на сайт Yaml. Там есть некоторые привязки python, документация и прочее.