config.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. # Flexlay - A Generic 2D Game Editor
  2. # Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com>
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. from typing import Any, Optional
  17. import configparser
  18. import os
  19. import logging
  20. import re
  21. class Config:
  22. """
  23. This class is used to store attributes which are
  24. needed again whenever the editor is run in a file
  25. Usage:
  26. from config import Config
  27. config = Config.create("config-test")
  28. # No spaces in attribute names!
  29. config.create_attribute("test_attribute", "")
  30. config.load()
  31. c
  32. """
  33. current = None
  34. attribute_pattern = "[a-zA-Z_][a-zA-Z0-9_]*"
  35. @staticmethod
  36. def create(projectname: str) -> 'Config':
  37. """Create a Config instance
  38. :param projectname: A dash-seperated, lower case name (will be saved in ~/.flexlay/projectname.cfg)
  39. :return: A Config instance
  40. """
  41. path = os.path.expanduser("~/.flexlay/")
  42. if not os.path.isdir(path):
  43. os.mkdir(path)
  44. return Config(projectname, os.path.join(path, projectname + ".cfg"))
  45. @staticmethod
  46. def check_valid(name: str) -> bool:
  47. """Check that a name for an attribute is valid
  48. :param name: Name to be checked
  49. :return: boolean, True if valid
  50. """
  51. match = re.match(Config.attribute_pattern, name)
  52. if match is not None and match.span()[1] == len(name):
  53. return True
  54. return False
  55. def __init__(self, project_name: str, filename: str) -> None:
  56. """Use Config.create() instead"""
  57. Config.current = self
  58. # We can't use self.attributes = {} as __setattr__ asks for self.attributes!
  59. self.__dict__["attributes"] = {} # The same as self.attributes = {}
  60. self.project_name = project_name
  61. self.filename = filename
  62. # Should be relevant to all editors.
  63. self.create_attribute("geometry", "")
  64. self.create_attribute("window_state", "")
  65. # Recent files is organised separately.
  66. self.recent_files: list[str] = []
  67. def __setattr__(self, key: str, value: Any) -> None:
  68. if key in self.attributes:
  69. self.attributes[key] = value
  70. else:
  71. super().__setattr__(key, value)
  72. def __getattr__(self, name: str) -> Any:
  73. if name in self.__dict__:
  74. return self.__dict__[name]
  75. elif name in self.attributes:
  76. return self.attributes[name]
  77. else:
  78. raise AttributeError("Config instance has no such attribute '" + name + "'")
  79. def load(self, filename: Optional[str] = None) -> None:
  80. """Load the configs from file
  81. You should run this function just after creating all the attributes you want.
  82. """
  83. if filename is None:
  84. filename = self.filename
  85. parser = configparser.ConfigParser()
  86. parser.read(filename)
  87. if self.project_name in parser:
  88. # Run through attributes, checking if they're in the parser's loaded variables.
  89. for key in self.attributes:
  90. if key in parser[self.project_name]:
  91. self.attributes[key] = parser[self.project_name][key]
  92. # Load recent files, listed in order as recent_file# = filename
  93. self.recent_files = []
  94. for i in range(0, 10):
  95. if ('recent_files%d' % i) in parser[self.project_name]:
  96. recent_file = parser[self.project_name]['recent_files%d' % i]
  97. self.recent_files.append(recent_file)
  98. else:
  99. logging.info("No " + self.project_name + " section found in " + filename)
  100. def save(self, filename: Optional[str] = None) -> None:
  101. """Save configs to file"""
  102. if filename is None:
  103. filename = self.filename
  104. parser = configparser.ConfigParser()
  105. parser[self.project_name] = {}
  106. for key in self.attributes:
  107. parser[self.project_name][key] = self.attributes[key]
  108. for i, recent_file in enumerate(self.recent_files):
  109. parser[self.project_name]['recent_files%d' % i] = recent_file
  110. with open(filename, "w") as fout:
  111. parser.write(fout)
  112. def create_attribute(self, name: str, default_value: str = "") -> None:
  113. """Create new attribute
  114. Can then be accessed like a member.
  115. e.g.
  116. config.create_attribute("jeff", "value")
  117. config.jeff = "second value"
  118. print(config.jeff)
  119. """
  120. if name not in self.attributes and name not in self.__dict__:
  121. if Config.check_valid(name):
  122. self.attributes[name] = default_value
  123. else:
  124. raise Exception("Attribute name \"" + name + "\" as it's not valid (must be a valid variable name)")
  125. else:
  126. raise Exception("Cannot create attribute \"" + name + "\", it already exists!")
  127. def add_recent_file(self, filename: str) -> None:
  128. """Add/move filename to top of recent files list.
  129. Max in list is 10, so remove first filename
  130. if necessary
  131. :param filename: file to add to list
  132. """
  133. if filename in self.recent_files:
  134. # Remove from list, to be appended at the end
  135. self.recent_files.pop(self.recent_files.index(filename))
  136. else:
  137. # Ensure only 9 recent files
  138. while len(self.recent_files) >= 10:
  139. self.recent_files.pop(0)
  140. self.recent_files.append(filename)
  141. # EOF #