Exemple #1
0
 def on_start(self):
     """
         Método chamado para inicializar o menu inicial
     :return: nada
     """
     Window.size = (300, 100)
     config = ConfigParser()
     # Carrega últimas configurações utilizadas (ou padrões se arquivo não é encontrado)
     config.read('settings.ini')
     self.username = config.get('section0', 'key00')
     self.color = SettingsApp.get_color(config.get('section0', 'key01'))
     if config.get('section1', 'key10') == "Single":
         self.is_multiplayer = False
     else:
         self.is_multiplayer = True
     self.match = config.get('section1', 'key11')
     self.rows = config.getint('section2', 'key20')
     self.columns = config.getint('section2', 'key21')
     self.bombs = config.getint('section3', 'key30')
Exemple #2
0
def load_config():
    global root, allow_uploads, port
    config = ConfigParser()
    config.read('serverconfig.ini')
    config.setdefaults('main', {
        'root': '/sdcard',
        'allow_uploads': False,
        'port': 11451
    })
    root = pathlib.Path(config['main']['root'])
    allow_uploads = config.getboolean('main', 'allow_uploads')
    port = config.getint('main', 'port')
class FileManager(BaseDialog):
    """
    :Events:
        `on_tab_switch`
            Called when switching tabs.
        `on_tap_file`
            Called when the file is clicked.
        `on_tap_dir`
            Called when the folder is clicked.
        `on_context_menu`
            Called at the end of any actions of the context menu,
            be it copying, archiving files and other actions.
        `on_open_plugin_dialog`
            Description.
        `on_dismiss_plugin_dialog`
            Description.
    """

    with open(
            os.path.join(os.path.dirname(__file__), "data",
                         "context-menu-items.json"),
            encoding="utf-8",
    ) as data:
        menu_right_click_items = ast.literal_eval(data.read())

    path = StringProperty(os.getcwd())
    """
    The path to the directory in which the file manager will open by
    default.

    :attr:`path` is an :class:`~kivy.properties.StringProperty`
    and defaults to ``os.getcwd()``.
    """

    context_menu_open = BooleanProperty(False)
    """
    Open or close context menu.

    :attr:`context_menu_open` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    path_to_skin = StringProperty()
    """
    Path to directory with custom images.

    :attr:`path_to_skin` is an :class:`~kivy.properties.StringProperty`
    and defaults to `''`.
    """

    bg_color = ListProperty()
    """
    Background color of file manager in the format (r, g, b, a).

    :attr:`bg_color` is a :class:`~kivy.properties.ListProperty`
    and defaults to `[]`.
    """

    bg_texture = StringProperty()
    """
    Background texture of file manager.

    :attr:`bg_texture` is a :class:`~kivy.properties.StringProperty` and
    defaults to `''`.
    """

    _overlay_color = ListProperty([0, 0, 0, 0])

    auto_dismiss = False

    _instance_file_chooser_icon = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.ext_files = {}
        # The object of the currently open tab.
        self.current_open_tab_manager = None
        # Open or closed the settings panel.
        self.settings_panel_open = False
        # Open or close the theme selection panel in the settings panel.
        self.settings_theme_panel_open = False
        # Open or close the dialog of plugin.
        self.dialog_plugin_open = False
        # Open or close dialog with search results.
        self.dialog_files_search_results_open = False

        self.instance_search_field = None

        self.config = ConfigParser()
        self.data_dir = os.path.join(os.path.dirname(__file__), "data")
        self.config.read(os.path.join(self.data_dir, "settings.ini"))

        self.register_event_type("on_tab_switch")
        self.register_event_type("on_tap_file")
        self.register_event_type("on_tap_dir")
        self.register_event_type("on_context_menu")
        self.register_event_type("on_open_plugin_dialog")
        self.register_event_type("on_dismiss_plugin_dialog")

        self.theme_cls.bind(theme_style=self.update_background_search_field)

        if self.path_to_skin and os.path.exists(self.path_to_skin):
            path_to_directory_exts = os.path.join(self.path_to_skin, "files")
            for name_file in os.listdir(path_to_directory_exts):
                self.ext_files[name_file.split(".")[0]] = os.path.join(
                    path_to_directory_exts, name_file)
        if not self.ext_files:
            with open(
                    os.path.join(os.path.dirname(__file__), "data",
                                 "default_files_type.json"),
                    encoding="utf-8",
            ) as data:
                self.ext_files = ast.literal_eval(data.read())

    def add_color_panel(self):
        def set_list_colors_themes(*args):
            self.settings_theme_panel_open = True
            if not theme_panel.content.ids.rv.data:
                for name_theme in palette:
                    theme_panel.content.ids.rv.data.append({
                        "viewclass":
                        "FileManagerSettingsColorItem",
                        "color":
                        get_color_from_hex(colors[name_theme]["500"]),
                        "text":
                        name_theme,
                        "manager":
                        self,
                    })

        # Adds a panel.
        theme_panel = MDExpansionPanel(
            icon="palette",
            content=Factory.FileManagerChangeTheme(),
            panel_cls=MDExpansionPanelOneLine(text="Select theme"),
        )
        theme_panel.bind(
            on_open=set_list_colors_themes,
            on_close=self._set_state_close_theme_panel,
        )
        self.ids.settings.add_widget(theme_panel)

        # Adds a close button to the settings panel.
        box = MDBoxLayout(adaptive_height=True)
        box.add_widget(Widget())
        box.add_widget(
            MDFlatButton(
                text="CLOSE",
                on_release=lambda x: self.hide_settings(theme_panel),
            ))
        self.ids.settings.add_widget(box)

    def apply_palette(self):
        """Applies the color theme from the settings file when opening the
        file manager window."""

        palette = self.config.get("General", "palette")
        theme = self.config.get("General", "theme")
        memorize_palette = self.config.getint("General", "memorize_palette")

        if memorize_palette:
            self.theme_cls.primary_palette = palette
            self.theme_cls.theme_style = theme

    def apply_properties_on_show_settings(self):
        """Applies the settings from the "settings.ini" file to the checkboxes
        of items on the settings panel."""

        self.ids.settings.ids.tooltip_check.active = self.config.getint(
            "General", "tooltip")
        self.ids.settings.ids.memorize_check.active = self.config.getint(
            "General", "memorize_palette")
        self.ids.settings.ids.theme_switch.active = (
            1 if self.theme_cls.theme_style == "Dark" else 0)

    def show_taskbar(self):
        def on_complete_animation(*args):
            self.ids.task_spinner.active = True
            self.ids.lbl_task.opacity = 1

        Animation(height=dp(24), d=0.2).start(self.ids.taskbar)
        Animation(user_font_size=sp(18), d=0.2).start(self.ids.button_expand)
        anim = Animation(opacity=1, d=0.2)
        anim.bind(on_complete=on_complete_animation)
        anim.start(self.ids.task_spinner)

    def hide_taskbar(self):
        def on_complete_animation(*args):
            self.ids.task_spinner.active = False
            self.ids.lbl_task.opacity = 0
            self.instance_search_field.text_field_search_dialog.ids.check_background.active = (
                False)
            self.instance_search_field.text_field_search_dialog.open()

        Animation(height=0, d=0.2).start(self.ids.taskbar)
        Animation(user_font_size=sp(1), d=0.2).start(self.ids.button_expand)
        anim = Animation(opacity=1, d=0.2)
        anim.bind(on_complete=on_complete_animation)
        anim.start(self.ids.task_spinner)

    def show_settings(self, instance_button):
        """Opens the settings panel."""

        self.apply_properties_on_show_settings()
        Animation(
            settings_container_y=self.ids.settings.height,
            d=0.2,
        ).start(self.ids.settings_container)
        Animation(
            _overlay_color=[0, 0, 0, 0.4],
            d=0.2,
        ).start(self)
        self.settings_panel_open = True

    def hide_settings(self, theme_panel):
        """Closes the settings panel."""
        def hide_settings(interval):
            Animation(settings_container_y=0,
                      d=0.2).start(self.ids.settings_container)
            self._set_state_close_theme_panel()

        if self.settings_theme_panel_open:
            theme_panel.check_open_panel(theme_panel)
        Clock.schedule_once(hide_settings, 0.5)
        Animation(
            _overlay_color=[0, 0, 0, 0],
            d=0.2,
        ).start(self)
        self.settings_panel_open = False

    def set_path(self, path):
        """Sets the directory path for the `FileChooserIconLayout` class."""

        self.path = path
        self.current_open_tab_manager.ids.file_chooser_icon.path = path
        tab_text = self.get_formatting_text_for_tab(os.path.split(path)[1])
        self.current_open_tab_manager.text = tab_text

    def get_formatting_text_for_tab(self, text):
        icon_font = fonts[-1]["fn_regular"]
        icon = md_icons["close"]
        text = f"[size=16][font={icon_font}][ref=]{icon}[/ref][/size][/font] {text}"
        return text

    def add_tab(self, path_to_file):
        """
        Adds a new tab in the file manager.

        :param path_to_file: The path to the file or folder that was right-clicked.
        """

        tab_text = self.get_formatting_text_for_tab(
            os.path.split(path_to_file)[1])
        tab = FileManagerTab(manager=self, title=tab_text, path=path_to_file)
        self._instance_file_chooser_icon = tab.ids.file_chooser_icon
        self.ids.tabs.add_widget(tab)
        self.current_open_tab_manager = tab
        self.ids.tabs.switch_tab(tab_text, search_by="title")
        self.path = path_to_file

    def remove_tab(
        self,
        instance_tabs,
        instance_tab_label,
        instance_tab,
        instance_tab_bar,
        instance_carousel,
    ):
        """Removes an open tab in the file manager.

        :param instance_tabs: <kivymd.uix.tab.MDTabs object>
        :param instance_tab_label: <kivymd.uix.tab.MDTabsLabel object>
        :param instance_tab: <__main__.Tab object>
        :param instance_tab_bar: <kivymd.uix.tab.MDTabsBar object>
        :param instance_carousel: <kivymd.uix.tab.MDTabsCarousel object>
        """

        for instance_tab in instance_carousel.slides:
            if instance_tab.text == instance_tab_label.text:
                instance_tabs.remove_widget(instance_tab_label)
                break

    def create_header_menu(self):
        """Creates a menu in the file manager header."""

        with open(
                os.path.join(os.path.dirname(__file__), "data",
                             "header_menu.json"),
                encoding="utf-8",
        ) as data:
            menu_header = ast.literal_eval(data.read())
            for name_icon_item in menu_header:
                self.ids.header_box_menu.add_widget(
                    MDIconButton(
                        icon=name_icon_item,
                        user_font_size="18sp",
                        disabled=True if name_icon_item
                        not in ("home", "settings") else False,
                        md_bg_color_disabled=(0, 0, 0, 0),
                    ))
        self.ids.header_box_menu.add_widget(
            MDSeparator(orientation="vertical"))
        self.ids.header_box_menu.add_widget(
            MDIconButton(
                icon="cog",
                user_font_size="18sp",
                on_release=self.show_settings,
            ))
        background_normal = os.path.join(
            self.data_dir,
            "images",
            "bg-field.png"
            if self.theme_cls.theme_style == "Light" else "bg-field-dark.png",
        )
        self.instance_search_field = FileManagerTextFieldSearch(
            background_normal=background_normal,
            background_active=background_normal,
            manager=self,
        )
        self.ids.header_box_menu.add_widget(Widget())
        self.ids.header_box_menu.add_widget(self.instance_search_field)

    def update_background_search_field(self, instance, value):
        background_normal = os.path.join(
            self.data_dir,
            "images",
            "bg-field.png" if value == "Light" else "bg-field-dark.png",
        )
        self.instance_search_field.background_normal = background_normal
        self.instance_search_field.background_active = background_normal

    def open_context_menu(self, entry_object, type_chooser):
        """Opens a context menu on right-clicking on a file or folder."""

        menu = MDDropdownMenu(
            caller=entry_object,
            items=self.get_menu_right_click(entry_object, type_chooser),
            width_mult=4,
            background_color=self.theme_cls.bg_dark,
            max_height=dp(240),
        )
        menu.bind(on_dismiss=self.context_menu_dismiss, )
        menu.open()
        self.context_menu_open = True

    def tap_on_file_dir(self, *touch):
        """Called when the file/dir is clicked."""

        type_click = touch[0][1].button
        # "FileChooserList" or "FileChooserIcon".
        type_chooser = touch[1]
        # FileThumbEntry object from file_chooser_icon.py file.
        entry_object = touch[0][0]

        if type_click == "right" and entry_object.path != "../":
            self.open_context_menu(entry_object, type_chooser)
        else:
            if entry_object.path == "../":
                entry_object.path = os.path.dirname(self.path)
            entry_object.collide_point(
                *touch[0]
                [1].pos) and self._instance_file_chooser_icon.entry_touched(
                    entry_object, touch[0][1])
            if os.path.isdir(entry_object.path):
                self.set_path(entry_object.path)
                self.dispatch("on_tap_dir", entry_object.path)
            else:
                self.dispatch("on_tap_file", entry_object.path)
        if hasattr(entry_object, "remove_tooltip"):
            entry_object.remove_tooltip()

    def call_context_menu_plugin(self, name_plugin, entry_object):
        module = importlib.import_module(
            f"kivymd_extensions.filemanager.libs.plugins.contextmenu")
        plugin_cls = module.ContextMenuPlugin(
            instance_manager=self,
            entry_object=entry_object,
        )
        plugin_cls.main(name_plugin)

    def tap_to_context_menu_item(self, text_item, entry_object):
        """
        :type entry_object:  <kivy.lang.builder.FileThumbEntry object>
        :type instance_item: <kivymd.uix.menu.MDMenuItemIcon object>
        :type instance_menu: <kivymd.uix.menu.MDDropdownMenu object>
        """

        for data_item in self.menu_right_click_items:
            if (list(data_item.items())
                    and text_item in list(data_item.items())[0]):
                if "cls" in data_item:
                    self.call_context_menu_plugin(data_item["cls"],
                                                  entry_object)
                    break

        if text_item == "Open in new tab":
            self.add_tab(entry_object.path)
            self.dismiss_context_menu()

    def dismiss_context_menu(self):
        if self.context_menu_open:
            for widget in Window.children:
                if isinstance(widget, MDDropdownMenu):
                    widget.dismiss()
                    break

    def context_menu_dismiss(self, *args):
        """Called when closing the context menu."""

        self.context_menu_open = False

    def get_menu_right_click(self, entry_object, type_chooser):
        """Returns a list of dictionaries for creating context menu items."""

        menu_right_click_items = []
        if type_chooser == "FileChooserIcon":
            for data_item in self.menu_right_click_items:
                if data_item:
                    icon = list(data_item.items())[0][0]
                    if icon:
                        viewclass = "FileManagerItem"
                        _txt_left_pad = dp(72)
                    else:
                        viewclass = "OneLineListItem"
                        _txt_left_pad = dp(32)
                    text = list(data_item.items())[0][1]
                    menu_right_click_items.append({
                        "text":
                        text,
                        "viewclass":
                        viewclass,
                        "icon":
                        icon,
                        "font_style":
                        "Caption",
                        "height":
                        dp(36),
                        "top_pad":
                        dp(4),
                        "bot_pad":
                        dp(10),
                        "divider":
                        None,
                        "_txt_left_pad":
                        _txt_left_pad,
                        "on_release":
                        lambda x=text, y=entry_object: self.
                        tap_to_context_menu_item(x, y),
                    })
        if type_chooser == "FileChooserList":
            menu_right_click_items.append({
                "viewclass":
                "OneLineListItem",
                "text":
                "Open in new tab",
                "font_style":
                "Caption",
                "height":
                dp(36),
                "divider":
                None,
                "top_pad":
                dp(4),
                "bot_pad":
                dp(10),
                "on_release":
                lambda x="Open in new tab", y=entry_object: self.
                tap_to_context_menu_item(x, y),
            })
        return menu_right_click_items

    def get_icon_file(self, path_to_file):
        """Method that returns the icon path for the file."""

        return self.ext_files.get(
            os.path.splitext(path_to_file)[1].replace(".", ""),
            os.path.join(self.path_to_skin, "file.png")
            if self.path_to_skin else "file-outline",
        )

    def update_files(self, instance_pludin_dialog, path):
        # FIXME: Unable to update directory. You have to go to a higher level
        #  and go back.
        self.set_path(os.getcwd())
        self.set_path(os.path.dirname(path))

    def is_dir(self, directory, filename):
        return os.path.isdir(os.path.join(directory, filename))

    def on_tap_file(self, *args):
        """Called when the file is clicked."""

    def on_tap_dir(self, *args):
        """Called when the folder is clicked."""

    def on_tab_switch(self, *args):
        """Called when switching tab."""

    def on_open_plugin_dialog(self, *args):
        self.dialog_plugin_open = True

    def on_dismiss_plugin_dialog(self, *args):
        self.dialog_plugin_open = False

    def on_context_menu(self, *args):
        """
        Called at the end of any actions of the context menu, be it copying,
        archiving files and other actions.
        """

    def on_open(self):
        """Called when the ModalView is opened."""

        self.add_tab(self.path)
        self.create_header_menu()
        self.apply_palette()
        self.add_color_panel()

    def _on_tab_switch(self, instance_tabs, instance_tab, instance_tab_label,
                       tab_text):
        self.current_open_tab_manager = instance_tab
        self.dispatch(
            "on_tab_switch",
            instance_tabs,
            instance_tab,
            instance_tab_label,
            tab_text,
        )

    def _set_state_close_theme_panel(self, *args):
        self.settings_theme_panel_open = False
Exemple #4
0
class Program(App, prog_class.ShowPlugin, prog_class.ShowThemesForum,
              prog_class.ShowSectionsForum, prog_class.ShowSendMailForm, 
              prog_class.ShowTopicsForum, prog_class.ShowListMail,
              prog_class.ShowFileDetails, prog_class.ShowUploadForm,
              prog_class.ShowFiles, prog_class.ShowUserProfile,
              ConnectionToServer):
    """Функционал программы."""

    # Языковая локализация - переменные с префиксом core.string_lang_.
    # Используются в классах библиотеки programclass. В текущем классе -
    # без ключевого слова self, так как были импортированы из programdata
    exec(open("{}/Data/Language/{}.txt".format(
        core.prog_path, language)).read().replace(
            "#key_text_color", core.theme_key_text_color.encode("u8")).replace(
            "#text_color", core.theme_text_color.encode("u8")).replace(
            "#link_color", core.theme_link_color.encode("u8")))

    # TODO: Константа CONST_SCREEN используется модулем kdialog для
    # вычесления ширины диалогового окна (Window.size[0] / CONST_SCREEN).
    # size_hint_x для этих целей неуместен. Например, при размере экрана
    # 600x300 и параметре size_hint_x=.5 окно будет слишком
    # узким и наоборот - 300x600 и тот же параметр size_hint_x - покажут окно
    # занимаемое бОльшую плоскость экрана.
    WIDTH, HEIGHT = Window.size
    if WIDTH <= HEIGHT:
        CONST_SCREEN = 1.2
    else:
        CONST_SCREEN = 2
    window_text_size = 15

    update = None  # нет обновлений
    messages_unread = None  # количество новых сообщений
    open_dialog = False  # если True - открыто диалоговое окно
    number_user = None  # номер участника на сайте
    cookie = None
    site = "http://m.dimonvideo.ru"
    previous_screen = None

    def __init__(self, **kvargs):
        super(Program, self).__init__(**kvargs)
        Window.bind(on_keyboard=self.events_program)

        # Для области видимомти в programclass.
        self.FadeTransition = FadeTransition
        self.Screen = Screen
        self.Clock = Clock
        # ----------------------------------
        self.parsing_xml = parsing_xml
        self.get_page = get_page
        self.set_cookie = set_cookie
        self.Manifest = Manifest
        self.core = core
        # ----------------------------------
        self.sendmail = sendmail
        self.setattachtoforum = setattachtoforum
        # ----------------------------------
        self.KDialog = KDialog
        self.FileChooser = FileChooser
        self.SelectColor = SelectColor
        self.CustomSpinner = CustomSpinner
        self.PageSendMail = PageSendMail
        self.ScrollButton = ScrollButton
        self.ThemesForum = ThemesForum
        self.AboutDialog = AboutDialog
        self.UploadFiles = UploadFiles
        self.MessageViewer = MessageViewer
        self.MailList = MailList
        self.BugReporter = BugReporter
        self.ImageViewer = ImageViewer
        self.customspinner = customspinner
        self._urllib = _urllib
        self.traceback = traceback
        # ----------------------------------
        self.clipboard = clipboard
        # ----------------------------------
        self.theme_decorator_window = core.theme_decorator_window

    def get_application_config(self):
        return super(Program, self).get_application_config(
            "{}/program.ini".format(core.prog_path))

    def build_config(self, config):
        config.adddefaultsection("General")
        config.setdefault("General", "showonstartpage", u"Новости сайта")
        config.setdefault("General", "language", u"Русский")
        config.setdefault("General", "showwarning", "1")
        config.setdefault("General", "resmessages", "1")
        config.setdefault("General", "authorization", "0")
        config.setdefault("General", "progresstextsize", "15")
        config.setdefault("General", "downloadkey", "0")
        config.setdefault("General", "downloadfolder",
                          {0: 'Downloads', 1: ''})
        config.setdefault("General", "user_reg",
                          {"login": "******", "password": "******"})

        config.adddefaultsection("Network")
        config.setdefault("Network", "authoconnect", "0")
        config.setdefault("Network", "checkattachtoforum", "1")
        config.setdefault("Network", "loadscr", "0")
        config.setdefault("Network", "authoupdate", "1")

        config.adddefaultsection("Mail")
        config.setdefault("Mail", "messagesread", "1")
        config.setdefault("Mail", "intervalscanmess", "10")

        config.adddefaultsection("Theme")
        config.setdefault("Theme", "theme", "blue_orange")
        config.setdefault("Theme", "edittheme", "0")
        config.setdefault("Theme", "createtheme", "0")

    def build_settings(self, settings):
        general_settings = open("{}/Data/Settings/general.json".format(
            core.prog_path)).read()
        general_data = general_settings % (
            core.string_lang_setting_show_on_home_page,
            core.string_lang_setting_show_on_home_page_title,
            core.string_lang_setting_show_on_home_page_desc,
            core.string_lang_setting_news,
            core.string_lang_setting_show_on_home_page_themes,
            core.string_lang_setting_language,
            core.string_lang_setting_language_title,
            core.string_lang_setting_language_desc,
            core.string_lang_setting_language_russian,
            core.string_lang_setting_language_english,
            core.string_lang_setting_font,
            core.string_lang_setting_font_title,
            core.string_lang_setting_font_desc,
            core.string_lang_setting_download,
            core.string_lang_setting_download_title,
            core.string_lang_setting_download_desc) % \
            self.downloadfolder[self.downloadkey]

        network_settings = open("{}/Data/Settings/network.json".format(
            core.prog_path)).read()
        network_data = network_settings % (
            core.string_lang_setting_connect,
            core.string_lang_setting_connect_title,
            core.string_lang_setting_connect_desc,
            core.string_lang_setting_update,
            core.string_lang_setting_update_title,
            core.string_lang_setting_update_desc,
            core.string_lang_setting_checkattachtoforum_title,
            core.string_lang_setting_checkattachtoforum_desc,
            core.string_lang_setting_loadscr_title,
            core.string_lang_setting_loadscr_desc)

        mail_settings = open("{}/Data/Settings/mail.json".format(
            core.prog_path)).read()
        mail_data = mail_settings % (
            core.string_lang_setting_mail,
            core.string_lang_setting_mail_title,
            core.string_lang_setting_mail_desc,
            core.string_lang_setting_mail_interval_title,
            core.string_lang_setting_mail_interval_desc)

        theme_settings = open("{}/Data/Settings/theme.json".format(
            core.prog_path)).read()
        theme_data = theme_settings % (
            core.string_lang_setting_chek_themes,
            core.string_lang_setting_chek_themes_title,
            core.string_lang_setting_chek_themes_desc,
            '", "'.join(os.listdir("{}/Data/Themes".format(core.prog_path))),
            core.string_lang_setting_edit_themes_title,
            core.string_lang_setting_edit_themes_desc,
            core.string_lang_setting_create_themes_title,
            core.string_lang_setting_create_themes_desc)

        settings.add_json_panel(core.string_lang_general,
                                self.config, data=general_data)
        settings.add_json_panel(core.string_lang_internet,
                                self.config, data=network_data)
        settings.add_json_panel(core.string_lang_mail[:-1],
                                self.config, data=mail_data)
        settings.add_json_panel(core.string_lang_theme,
                                self.config, data=theme_data)

    def on_config_change(self, config, section, key, value):
        """Вызывается при выборе одного из пункта настроек программы."""

        def select_callback(*args):
            file_manager.body.dismiss()
            self.downloadfolder[1] = file_manager.select_folder
            config.set("General", "downloadkey", "1")
            config.set("General", "downloadfolder", str(self.downloadfolder))
            config.write()

        # TODO: обновить описание раздела на путь к выбранной директориии.
        if key == "downloadkey" and int(value):  # "Настройки загрузок"
            file_manager = \
                FileChooser(select_callback=select_callback, filter="folder",
                            title=core.string_lang_select_folder,
                            background_image=self.core.theme_decorator_window,
                            auto_dismiss=False,  size=(.85, .9),)
        elif key == "downloadkey" and not int(value):
            self.downloadfolder[1] = ""
            config.set("General", "downloadkey", "0")
            config.set("General", "downloadfolder", str(self.downloadfolder))
            config.write()
        elif key == "checkattachtoforum":  # "Проверка формы аттача"
            self.checkattachtoforum = int(value)
        elif key == "progresstextsize":  # "Настойка размера шрифта"
            self.window_text_size = int(value)
        elif key == "edittheme":  # "Правка палитры установленной темы"
            self.edit_pallete_theme()

    def on_pause(self):
        """Ставит приложение на 'паузу' при выхоже из него.
        В противном случае запускает программу по заново"""

        return True

    def build(self):
        self.title = core.string_lang_title[:-1]  # заголовок окна программы
        self.icon = "Data/Images/logo.png"  # иконка окна программы
        self.use_kivy_settings = False

        self.config = ConfigParser()
        self.config.read("{}/program.ini".format(core.prog_path))
        self.beep = SoundLoader.load("Data/mess_unread.wav")
        self.set_variable_from_settings()

        # Домашняя страница клиента.
        self.start_screen = \
            StartScreen(name_buttons_menu=core.name_buttons_menu,
                        buttons_menu=core.buttons_menu,
                        buttons_group=core.buttons_group,
                        previous="Data/Images/logo.png",
                        title_previous=core.string_lang_title[:-1],
                        title_image="Data/Images/DV.png",
                        overflow_image="Data/Images/overflow_image.png",
                        previous_image="Data/Images/previous_image.png",
                        events_callback=self.events_program)
        self.screen = self.start_screen

        Clock.schedule_once(self.read_license, 3)
        return self.start_screen

    def edit_pallete_theme(self):
        print("edit_pallete_theme")

    def set_variable_from_settings(self):
        self.messagesread = self.config.getint(
            "Mail", "messagesread")
        self.intervalscanmess = self.config.getint(
            "Mail", "intervalscanmess")

        self.loadscr = self.config.getint(
            "Network", "loadscr")
        self.checkattachtoforum = self.config.getint(
            "Network", "checkattachtoforum")
        self.authoupdate = self.config.getint(
            "Network", "authoupdate")
        self.authoconnect = self.config.getint(
            "Network", "authoconnect")

        self.downloadkey = self.config.getint(
            "General", "downloadkey")
        self.downloadfolder = eval(self.config.get(
            "General", "downloadfolder"))
        self.window_text_size = self.config.getint(
            "General", "progresstextsize")
        self.language = core.select_locale[self.config.get(
            "General", "language")]
        self.showonstartpage = self.config.get(
            "General", "showonstartpage")
        self.authorization = self.config.getint(
            "General", "authorization")
        self.resmessages = self.config.getint(
            "General", "resmessages")
        _user_reg = eval(self.config.get(
            "General", "user_reg"))

        try:
            self.user_reg = {"login": _user_reg["login"].decode("hex"),
                             "password": _user_reg["password"].decode("hex")}
        except TypeError:
            self.user_reg = {"login": _user_reg["login"],
                             "password": _user_reg["password"]}
        except AttributeError:  # Python 3
            self.user_reg = {"login": _user_reg["login"],
                             "password": bytes.fromhex(
                                 _user_reg["password"]).decode('utf-8')}

    def read_license(self, *args):
        def dialog_answer_handler(*args):
            """Функция обработки сигналов диалогового окна.

            type answer: str;
            param answer: имя нажатой кнопки или введенного текта;

            """

            if len(args) == 1:  # нажата кнопка
                answer = args[0]

                if answer == core.string_lang_yes:
                    open("{}/README.md".format(core.prog_path), "w").write(
                        re.sub('\[.*?]', '', license).replace("\n\n\n", "\n"))
                    self.open_dialog = False

                    # Устанавливаем подпись на домашней странице клиента -
                    # "Мобильный клиент сайта dimonvideo.ru"
                    self.start_screen.mobile_client_label.text = \
                        core.string_lang_mobile_client_label

                    # Автоматическое подключение при старте.
                    if self.authoconnect \
                            and self.user_reg["login"] != "login" \
                            and self.user_reg["password"] != "password":
                        Clock.schedule_once(self.connect, 0)
                elif answer == core.string_lang_no:
                    sys.exit(1)
            else:  # нажата ссылка
                link = args[1]
                webbrowser.open(link)

        # Если пользователь уже был авторизирован.
        if self.authorization or os.path.exists("{}/README.md".format(
                core.prog_path)):
            if self.authoconnect and self.user_reg["login"] != "login" and \
                    self.user_reg["password"] != "password":
                self.connect()
            else:
                self.start_screen.mobile_client_label.text = \
                    core.string_lang_mobile_client_label
        else:
            self.open_dialog = True
            license = \
                core.string_lang_license.replace(
                    "--------------------------------------------------------"
                    "-----------------------", "").split(
                    "[color={}]ОПИСАНИЕ ".format(core.theme_key_text_color))[0]
            # Спрашиваем, согласен ли пользователь с условиями.
            self.create_window(callback=dialog_answer_handler, size_x=1.2,
                               text=license, button_ok=core.string_lang_yes,
                               button_no=core.string_lang_no, param="query")

    def events_program(self, *args):
        """Вызывается при выборе одного из пунктов меню программы."""

        if self.open_dialog:
            return
        if len(args) == 2:  # нажата ссылка
            event = args[1].encode("utf-8")
        else:  # нажата кнопка
            try:
                _args = args[0]
                event = _args if isinstance(_args, str) else _args.id
            except AttributeError:  # ввод текста в форму отправки сообщений
                return

        #if not self.cookie and event == core.string_lang_item_menu_profile:
        #    self.create_window(core.string_lang_authorization_client)
        #    return
        # ------------------------------ВОЙТИ----------------------------------
        if event == core.string_lang_item_menu_connect:
            self.connect()
        # -----------------------------ПРОФИЛЬ---------------------------------
        if event == core.string_lang_item_menu_profile:
            progress = self.create_window(core.string_lang_info_your,
                                          param="loaddialog")
            Clock.schedule_once(lambda *args: self.show_user_profile(
                progress, self.user_reg["login"]), 0.1)
        # ----------------------------ACTION BAR-------------------------------
        elif event == "previous" or event in (1000, 27):
            self.back_screen()
        # -----------------------------НАСТРОЙКИ------------------------------
        elif event == core.string_lang_item_menu_settings:
            self.open_settings()
        # -----------------------------ЛИЦЕНЗИЯ-------------------------------
        elif event == core.string_lang_item_menu_license:
            self.create_window(
                callback=self.show_license, size_x=1.2,
                text=core.string_lang_license.split(
                    "[color={}]Нажав ".format(
                        core.theme_text_color))[0].replace(
                    "\n--------------------------------------------"
                    "-----------------------------------", ""),
                button_ok=core.string_lang_on_russian,
                button_no=core.string_lang_on_english, param="query",
                dismiss=True)
        # ----------------------------О ПРОГРАММЕ------------------------------
        elif event == core.string_lang_item_menu_about:
            self.show_about()
        elif event == core.string_lang_item_menu_exit:
            sys.exit(0)
        # ------------------------------ФОРУМ----------------------------------
        elif event == "forum":
            progress = self.create_window(text=core.string_lang_load_topics,
                                          param="loaddialog")
            Clock.schedule_once(
                lambda *args: self.show_sections_forum(
                    progress, core.api.ROOT_PARTITION_FORUM), 1)
        # ----------------------ДОБАВИТЬ ФАЙЛ НА СЕРВЕР------------------------
        elif event == "upload":
            self.show_upload_form()
        # ----------------------------СООБЩЕНИЯ--------------------------------
        elif event == "mail":
            def _show_list_mail(*args):
                progress = \
                    self.create_window(text=core.directions[args[0][0].id][1],
                                       param="loaddialog")
                Clock.schedule_once(
                    lambda *arg: self.show_list_mail(progress, args[0]), 2)

            # Просим авторизацию.
            if not self.cookie:
                self.create_window(text=core.string_lang_authorization_client)
                return

            # Выводим папки для просмотра и функции для манипуляции с почтой.
            scroll_mail = \
                ScrollButton(
                    events_callback=lambda *args: _show_list_mail(args),
                    button_list=core.sections_mail, dismiss=True,
                    background_image=core.theme_decorator_window)
            scroll_mail.show(title=core.string_lang_mail)
        # ----------------------------СТАТЬИ-------------------------------
        elif event == "articles":
            progress = self.create_window(text=core.string_lang_load_topics)
            Clock.schedule_once(
                lambda *args: self.show_file_details(
                    progress, "articles"), 0.1)
        # ---------------------------ПЛАГИНЫ--------------------------------
        elif event == core.string_lang_plugin:
            self.show_plugins()
        # ----------------------------ФАЙЛЫ---------------------------------
        elif event == "files":
            self.show_sections_files()

        return True

    def back_screen(self):
        """Показываем предыдущий и удаляем из списка текущий экран."""

        if len(self.screen.screen_manager.screens) != 1:
            self.screen.screen_manager.screens.pop()
        self.screen.screen_manager.current = \
            self.screen.screen_manager.screen_names[-1]
        # Устанавливаем имя предыдущего экрана.
        self.screen.action_previous.title = self.screen.screen_manager.current

    def create_window(self, text, title=core.string_lang_title,
                      button_ok=None, button_no=None, button_cancel=None,
                      background=core.theme_decorator_window, param="info",
                      callback=p, dismiss=False, size_x=CONST_SCREEN,
                      font_size=window_text_size, password=False):
        window = KDialog(answer_callback=callback, base_font_size=font_size,
                         background_image=background, size_hint_x=size_x)
        window.show(title=title, image="Data/Images/loading.gif",
                    text=text, param=param, auto_dismiss=dismiss,
                    text_button_ok=button_ok, text_button_no=button_no,
                    text_button_cancel=button_cancel, password=password)
        return window

    def create_text_topics_or_comments(self, text_topics_or_comments,
                                       dict_info_topics_or_comments,
                                       current_number_page, number_posts,
                                       next_answers, flag):
        """Вызывается из класса ShowTopicsForum и ShowFileDetails
        для формирования текста страниц форума/комментариев/статей/описания
        к файлам.

        :param text_topics_or_comments: сырой текст страницы, полученный от
                                        сервера;

        :param dict_info_topics_or_comments:
                            информация о каждом посте/комментена станице вида
                            {"id": ["12345", ...],
                             "author": ["name_author", ...],
                             "date": ["12345", ...]}

        :type current_number_page: int;
        :param current_number_page: выбраная страница;

        :type numbers_pages_forum: int;
        :param numbers_pages_forum: количество страниц форума/комментариев;

        :type next_answers: str;
        :param type next_answers: "0", "20", "40", "60", ... - вывод следующей
                                   двадцатки топиков

        :type flag: str or None;
        :param flag: имя раздела, если выводим комментарии к файлу;

        Returns: (str, str, list):

        - форматированый текст модулем textmarker.py страницы форума или
        комментвриев;
        - строку, пагинатор страниц, маркированую для MarkupLabel;
        - список ссылок на странице, вида [('адрес_ссылки', 'имя_ссылки'),];

        """

        _mark_links = []
        _next_answers = next_answers
        _list_topics_or_comments = text_topics_or_comments.split("</text>")
        list_topics_or_comments = []
        text_topics_or_comments = ""
        online = ""

        for i, author in enumerate(dict_info_topics_or_comments["author"]):
            # Парсинг топика/комментария.
            topic_or_comment_parse, mark_links, mark_links_scr = \
                self.parse_text(_list_topics_or_comments[i].decode("cp1251"))
            _mark_links += mark_links.items()

            if flag:  # для комментариев
                _time = dict_info_topics_or_comments["date"][i]
            else:
                _time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(
                    int(dict_info_topics_or_comments["date"][i])))
            _next_answers += 1

            if not flag:  # для форума
                try:
                    if dict_info_topics_or_comments["online"][i] == "0":
                        online = "|[color=#f70000]Offline[/color]|"
                    else:
                        online = "|[color=#00d700]Online[/color]|"
                except KeyError:  # для комментариев
                    online = ""

            # Если топик пользователя, добавляем пункт "Удалить" и
            # подсвечивает другим цветом ник.
            if author == self.user_reg["login"]:
                author_color = core.theme_key_text_color
                item_delete = "[color={}][ref=Post_id: {}]{}[/ref][/color]|" \
                              "".format(core.theme_link_color,
                                        dict_info_topics_or_comments["id"][i],
                                        core.string_lang_delete)
            else:
                author_color = core.theme_link_color
                item_delete = ""

            # #1 Author | online | Ответ | Копировать | Удалить
            # 26.01.16 10:45
            #
            # Текст топика...
            topic_or_comment_parse = \
                "#{} [color={author_color}][ref={author}]{author}[/ref]" \
                "[/color] {}|[color={link_color}][ref={answer}-|-{author}" \
                "-|-{i}]{answer}[/ref][/color]|[color={link_color}]" \
                "[ref={copy}-|-{author}-|-{i}]{copy}[/ref][/color]|" \
                "{delete}\n{}\n-------------\n{}\n-------------".format(
                    str(_next_answers), online, _time, topic_or_comment_parse,
                    author=author, answer=core.string_lang_answer, i=str(i),
                    copy=core.string_lang_copy, delete=item_delete,
                    author_color=author_color,
                    link_color=core.theme_link_color)

            text_topics_or_comments += topic_or_comment_parse
            list_topics_or_comments.append(topic_or_comment_parse)

        # Если топиков менбше 20-ти.
        number_posts = 1 if not number_posts else number_posts
        current_number_page = 1 if not current_number_page \
            else current_number_page
        # Список для хранения номеров страниц.
        list_pages = paginator(number_posts, current_number_page)

        # Формируем нумерацию страниц и помечаем выбраную.
        build_pages = ""

        for number_page in list_pages:
            try:
                number_page = \
                    int(number_page.replace("[", "").replace("]", ""))

                if current_number_page == number_page:
                    color = core.theme_key_text_color
                else:
                    color = core.theme_link_color
            except ValueError:
                color = core.theme_link_color

            build_pages += \
                "[color={}][ref=Page_number: {number_page}]{number_page}" \
                "[/ref]  ".format(color, number_page=number_page)

        return text_topics_or_comments, list_topics_or_comments, \
               build_pages, _mark_links

    def refs_callback(self, *args):
        """Обрабатывает события при клике на страницах корневых разделов
        тем форума или списка файлов."""

        # Выводим следующую страницу тем форума или списка файлов.
        if "Page_number" in args[0]:
            callback_load_new_page = args[5]
            select_number_page = args[0].split(": ")[1]
            callback_load_new_page(select_number_page)
            return

        # Выбран файл из списка на экране или ссылка "Комментариев:" под ним.
        if not args[1]:
            select_name_file = args[0]
            self.load_file_details(None, select_name_file)
            return

        try:
            forum_name = args[0]  # "Python"
            themes_id = args[1][forum_name]  # "141", "1728141632" (tid, id)

            if len(args[2].items()):
                forum_id = args[2][forum_name]  # "id_форума"
            else:
                forum_id = themes_id

            forum_flag = args[3]  # "themes" or "topics"
            forum_id_posts = args[4]  # {'1728141632': '9767', ...}
            self.tid_forum = forum_id

            # Количество ответов в форуме.
            try:
                number_posts = forum_id_posts[themes_id]
            except KeyError:
                number_posts = None

            if forum_flag == "topics":
                progress = \
                    self.create_window(text=core.string_lang_load_themes_answer)
                Clock.schedule_once(lambda *arg: self.show_topics_forum(
                    progress, forum_name, themes_id, 0, int(number_posts) / 20,
                    1), 1)
            else:
                progress = \
                    self.create_window(text=core.string_lang_load_themes)
                Clock.schedule_once(lambda *arg: self.show_themes_forum(
                    progress, forum_name, forum_flag, themes_id,
                    core.api.THEMES_FORUM), 1)
        except KeyError:
            # Клик на ник пользователя в разделе "Последний ответ".
            user_name = args[0]
            self.send_mail_or_show_profile(user_name)

    def send_mail_or_show_profile(self, user_name, instance_open_mail=None):
        """Выводит окно с кнопками показа профиля или формы для отправки
        сообщения выбранного пользователя.

        type instance_open_mail: <class 'messageviewer.MessageViewer'>;
        param instance_open_mail: открытое окно с текстом просматриваемого
                                  сообщения;
        """

        def _send_mail_or_show_profile(*args):
            name_button = args[0][0]  # "Профиль"/"Сообщение"

            if name_button == core.string_lang_item_menu_profile:
                progress = \
                    self.create_window(text=core.string_lang_info_your)
                Clock.schedule_once(lambda *arg: self.show_user_profile(
                    progress, user_name, instance_open_mail), 1)
            elif name_button == core.string_lang_write_message_status:
                self.send_mail(user_name)

        self.create_window(
            callback=lambda *args: _send_mail_or_show_profile(args),
            param="query", dismiss=True,
            text=core.string_lang_username.format(user_name),
            button_ok=core.string_lang_item_menu_profile,
            button_cancel=core.string_lang_write_message_status)

    def error_read_page(self, progress=None):
        if progress:
            progress.body.dismiss()
        self.create_window(text=core.string_lang_error_read_page)
        self.open_dialog = False

    def get_news(self):
        """Возвращает текст новостей сайта или False, если их нет."""

        news = get_page(core.api.GET_NEWS, self.cookie)
        if not news:
            self.error_read_page()
            return

        news = core.dict_search_templates["name"].findall(news)[0]
        if news != "":
            return news
        else:
            if platform != "android":
                print("Новостей нет!")
                print("------------------------------------")
            return False

    def download_file(self, link_text, link_address):
        def download_cancel(self, *args):
            setattr(retrieve_progress_load, "flag", 0)
            progress_load.body.dismiss()

        path_to_folder = self.downloadfolder[self.downloadkey]
        if not os.path.exists(path_to_folder):
            os.mkdir(path_to_folder)

        progress_load = \
            ProgressLoad(retrieve_callback=retrieve_progress_load,
                         events_callback=download_cancel,
                         text_button_cancel=core.string_lang_button_cancel,
                         text_already_loaded=core.string_lang_already_loaded,
                         text_total_size=core.string_lang_total_size)
        progress_load.show(link_address,
                           "{}/{}".format(path_to_folder,
                                          os.path.split(link_address)[1]))

    def update_page_on_forum(self, next_answers=0, current_number_page=0,
                             *args):
        # После добавления/удаления ответа в форум закрываем текущую страницу
        # и удаляем ее из списка экранов, чтобы с последующим вызовом
        # функции self.show_topics_forum была выведена обновленая
        # страница форума с последним ответом.

        name_forum = args[0]
        forum_id = args[1]
        number_posts = args[2]
        flag = args[3]  # если None - обновляем последнюю страницу форума -
        # добавление нового поста, если True - текущую - удаление поста

        self.screen.screen_manager.screens.pop()
        self.screen.screen_manager.current = \
            self.screen.screen_manager.screen_names[-1]

        progress = self.create_window(text=core.string_lang_forum_update)

        if not flag:  # если происходит добавление поста в форум
            next_answers = number_posts * 20
            current_number_page = number_posts
        if isinstance(flag, str):  # раздел файлов, например "articles"
            flag = flag

        Clock.schedule_once(lambda *args: self.show_topics_forum(
            progress, name_forum, forum_id, next_answers, number_posts,
            current_number_page, flag), 1)

    def update_program(self):
        """Проверяет наличие обновлений на сервере github,com.
        Проверяестя версия программы, версии плагинов, наличие измененных
        программых файлов."""

        if platform != "android":
            print("Проверка обновлений:")
            print("------------------------------------")

        temp_path = "{}/Data/temp".format(core.prog_path)

        if not os.path.exists(temp_path):
            os.mkdir(temp_path)

        update_file = urllib.urlopen(
            "https://github.com/HeaTTheatR/HeaTDV4A/raw/master/"
            "Data/uploadinfo.ini").read()
        open("{}/uploadinfo.ini".format(temp_path), "w").write(
            update_file)

        config_update = ConfigParser()
        config_update.read("{}/uploadinfo.ini".format(temp_path))

        info_list_update = []

        new_version_program = \
            config_update.getfloat("info_update", "new_version_program")
        updated_files_program = \
            eval(config_update.get("info_update", "updated_files_program"))
        dict_official_plugins_program = \
            config_update.items("plugin_update")
        official_plugins_program = dict(dict_official_plugins_program)

        install_user_plugins = self.started_plugins
        current_version_program = __version__

        if platform != "android":
            print("Проверка версии программы ...")
            print("Проверка актуальности плагинов ...")

        for plugin in install_user_plugins.keys():
            try:
                info_official_plugin = eval(official_plugins_program[plugin])
                if info_official_plugin[plugin]['plugin-version'] > \
                        install_user_plugins[plugin]['plugin-version']:
                    info_list_update.append(info_official_plugin)

                    if platform != "android":
                        print("Плагин '{}' обновился до версии '{}'!".format(
                            plugin, info_official_plugin[plugin][
                                'plugin-version']))
            except KeyError:
                continue

        if platform != "android":
            print("Проверка обновлений завершена ...")
            print("------------------------------------")
            print()

        if len(info_list_update) or new_version_program > \
                current_version_program:
            self.update = True

            if platform != "android":
                print("Доступны обновления!")
                print("------------------------------------")
                print()
        else:
            if platform != "android":
                print("Обновлений нет!")
                print("------------------------------------")
                print()

    def set_password(self):
        """Установка данных пользователя."""

        def write_password(data):
            _user_reg = {"login": data[0].encode("hex"),
                         "password": data[1].encode("hex")}
            self.open_dialog = False

            # Если данные не введены.
            if _user_reg["login"] == "" or _user_reg["password"] == "":
                return
            else:
                self.config.set("General", "user_reg", _user_reg)
                self.config.write()

                self.user_reg["login"] = data[0]
                self.user_reg["password"] = data[1]

                registration_form.body.dismiss()
                self.Clock.schedule_once(lambda *args: self.connect(args), 0.2)

        self.open_dialog = True
        registration_form = \
            self.create_window(callback=write_password, size_x=self.CONST_SCREEN,
                               text=core.string_lang_input_password, password=True,
                               button_ok=core.string_lang_yes, param="logpass")

    def parse_text(self, html_text):
        """Маркировка в тексте ссылок, спойлеров, программного кода.

        Returns: (str, dict);

        - отмаркерованый текст страницы;
        - {'адрес_ссылки': 'имя ссылки', ...};

        """

        def replace(text):
            parse_text = text.replace(
                "**  ", "**", 1)
            parse_text = parse_text.replace(
                "  **", "**", 1)
            parse_text = parse_text.replace(
                "-------------", "\n-------------\n")
            parse_text = parse_text.replace(
                "======================", "\n======================\n")
            parse_text = parse_text.replace(
                "======================", "#-----------SEPARATOR")
            parse_text = parse_text.replace(
                "============", "#-----------SEPARATOR")
            parse_text = parse_text.replace(
                "<![CDATA[", "")
            parse_text = parse_text.replace(
                "{}{}{}".format(
                    core.mark_tag["refs"][0], core.string_lang_show_comments,
                    core.mark_tag["refs"][1]),
                "-------------\n{}{}{}".format(
                    core.mark_tag["refs"][0],
                    core.string_lang_show_comments,
                    core.mark_tag["refs"][1]))
            parse_text = parse_text.replace("\t", "").strip()
            return parse_text

        text = TextMarker(core.mark_tag["refs"], core.mark_tag["spoiler"],
                          core.mark_tag["code"], core.mark_tag["scr"],
                          core.language_code, html_text)
        text_with_mark_scr = text.mark_image(html_text.encode("utf-8"))
        text_with_mark_code = text.mark_code(text_with_mark_scr)
        text_with_mark_links = text.mark_links(text_with_mark_code)
        parse_message = html_parser(text_with_mark_links)
        text_with_mark_spoilers = text.mark_spoilers(parse_message)
        parse_text = replace(text_with_mark_spoilers)

        return parse_text, text.dict_link, text.dict_link_scr

    def set_screen_message_viewer(self, name_screen, text_for_screen,
                                  list_links, list_links_scr, build_pages,
                                  flag, dict_info=None, events_callback=None,
                                  load_scr=True):
        """Открывает новый экран MessageViewer."""

        if not dict_info:
            dict_info = {}
        background = "Data/Images/background.png"

        list_text_button = [self.core.string_lang_answer_on_mail,
                            self.core.string_lang_delete,
                            self.core.string_lang_in_archive]
        background_code = [0.06666666666666667, 0.0784313725490196,
                           0.08235294117647059, 1.0]
        foreground_code = [0.43137254901960786, 0.49019607843137253,
                           0.5686274509803921, 1.0]
        border_code = [1.0, 0.7725490196078432, 0.09803921568627451, 1.0]

        message_viewer = self.MessageViewer(
            event_callback=events_callback,
            text_message=text_for_screen,
            load_scr=load_scr,
            spoilers_mark_tag=core.mark_tag["spoiler"],
            code_mark_tag=core.mark_tag["code"],
            links_mark_tag=core.mark_tag["refs"],
            scr_mark_tag=core.mark_tag["scr"],
            message_links=dict(list_links),
            message_links_scr=list_links_scr,
            forum=flag, dict_info=dict_info,
            list_text_button=list_text_button,
            background_code=background_code,
            foreground_code=foreground_code,
            border_code=border_code,
            size_hint=(.9, .89),
            number_page=build_pages,
            background=background,
            button_add_answer_background_normal="Data/Images/button_blue.png",
            button_add_answer_background_down="Data/Images/button_orange.png",
            text_button_add_answer=self.core.string_lang_add_answer_in_forum)

        if flag != "mail":
            screen = self.Screen(name=name_screen)
            screen.add_widget(message_viewer)
            self.screen.screen_manager.add_widget(screen)
            self.screen.screen_manager.transition = self.FadeTransition()
            self.screen.screen_manager.current = name_screen
            self.screen.action_previous.title = name_screen

        return message_viewer

    def scan_new_messages(self, interval):
        messages = get_page(core.api.NUMBER_UNREAD_MESS, self.cookie)

        if messages:
            total_unread_messages = \
                core.dict_search_templates["unread"].findall(messages)[0]

            if int(total_unread_messages) > int(self.resmessages):
                self.resmessages = total_unread_messages
                self.alert_new_messages(total_unread_messages)
                self.config.set("General", "resmessages",
                                total_unread_messages)
                self.config.write()

    def alert_new_messages(self, total_unread_messages):
        self.beep.play()
        self.create_window(text=core.string_lang_new_messages.format(
            total_unread_messages))

    def show_license(self, *args):
        def show_license(progress, on_language):
            text_license = open("LICENSE/GNU_LICENSE_{}.rst".format(
                core.dict_language[on_language])).read()

            message = KDialog(underline_color="a6b4bcff",
                              base_font_size=self.window_text_size,
                              background_image=core.theme_decorator_window)
            message.show(title="GNU_LICENSE:", text=text_license, rst=True,
                         size_rst=(.9, .7))
            progress.body.dismiss()

        if len(args) > 1:  # выбраны ссылки в тексте
            click_link = args[1]
            webbrowser.open(click_link)
            return
        else:
            on_language = args[0]  # кнопки 'На русском/На английском'

        progress = self.create_window(text=core.string_lang_wait)
        Clock.schedule_once(lambda *args: show_license(progress, on_language),2)

    def show_about(self):
        def events_callback(instance_label, text_link):
            def answer_callback(answer):
                if answer in [core.string_lang_click_dimonvideo_redirect,
                              core.string_lang_click_ptyhon_redirect,
                              core.string_lang_click_kivy_redirect]:
                    webbrowser.open(answer.replace("www.", r"http:\\"))

            if text_link in ["HeaTTheatR", "Virtuos86", "dimy44"]:
                Clock.schedule_once(
                    lambda *args: self.send_mail_or_show_profile(text_link),
                    0.1)
            else:
                self.create_window(
                    callback=answer_callback, param="query",
                    text=core.text_for_about[text_link]["text"],
                    button_ok=core.text_for_about[text_link]["ok"],
                    button_cancel=core.text_for_about[text_link]["cancel"])

        AboutDialog(events_callback=events_callback,
                    background_image=core.theme_decorator_window,
                    name_program=core.string_lang_about_name_program,
                    logo_program="Data/Images/logo.png",
                    info_program=core.info_program)
Exemple #5
0
class UserPrefs(EventDispatcher):
    '''
    A class to manage user preferences for the RaceCapture app
    '''
    DEFAULT_DASHBOARD_SCREENS = ['5x_gauge_view', 'laptime_view', 'tach_view', 'rawchannel_view']
    DEFAULT_PREFS_DICT = {'range_alerts': {},
                          'gauge_settings':{},
                          'screens':DEFAULT_DASHBOARD_SCREENS,
                          'alerts': {}}

    DEFAULT_ANALYSIS_CHANNELS = ['Speed']

    prefs_file_name = 'prefs.json'

    def __init__(self, data_dir, user_files_dir, save_timeout=0.2, **kwargs):
        self._prefs_dict = UserPrefs.DEFAULT_PREFS_DICT
        self.config = ConfigParser()
        self.data_dir = data_dir
        self.user_files_dir = user_files_dir
        self.prefs_file = path.join(self.data_dir, self.prefs_file_name)
        self.register_event_type("on_pref_change")
        self.load()

    def on_pref_change(self, section, option, value):
        pass

    def set_range_alert(self, key, range_alert):
        '''
        Sets a range alert with the specified key
        :param key the key for the range alert
        :type string
        :param range_alert the range alert
        :type object
        '''
        self._prefs_dict["range_alerts"][key] = range_alert
        self.save()

    def get_range_alert(self, key, default=None):
        '''
        Retrives a range alert for the specified key
        :param key the key for the range alert
        :type key string
        :param default the default value, optional
        :type default user specified
        :return the range alert, or the default value 
        '''
        return self._prefs_dict["range_alerts"].get(key, default)


    def get_alertrules(self, channel):
        '''
        Retrieve the alert_rules for the specified channel. If the
        alertrules do not exist for the specified channel, return an empty
        default AlertRuleCollection
        :return AlertRuleCollection
        '''
        alertrules = self._prefs_dict['alerts'].get(channel)
        if alertrules is None:
            alertrules = AlertRuleCollection(channel, [])
            self._prefs_dict['alerts'][channel] = alertrules

        return alertrules

    def set_alertrules(self, channel, alertrules):
        self._prefs_dict['alerts'][channel] = alertrules
        self.save()

    def set_gauge_config(self, gauge_id, config_value):
        '''
        Stores a gauge configuration for the specified gauge_id
        :param gauge_id the key for the gauge
        :type gauge_id string
        :param config_value the configuration value to set
        :type config_value string
        '''
        self._prefs_dict["gauge_settings"][gauge_id] = config_value
        self.save()

    def get_gauge_config(self, gauge_id):
        '''
        Get the gauge configuration for the specified gauge_id
        :param gauge_id the key for the gauge
        :type string
        :return the gauge configuration
        '''
        return self._prefs_dict["gauge_settings"].get(gauge_id, False)

    def get_dashboard_screens(self):
        return copy(self._prefs_dict['screens'])

    def set_dashboard_screens(self, screens):
        self._prefs_dict['screens'] = copy(screens)
        self.save()

# Regular preferences below here

    def get_last_selected_track_id(self):
        return self.get_pref('track_detection', 'last_selected_track_id')

    def get_last_selected_track_timestamp(self):
        return self.get_pref_int('track_detection', 'last_selected_track_timestamp')

    def get_user_cancelled_location(self):
        return self.get_pref('track_detection', 'user_cancelled_location')

    def set_last_selected_track(self, track_id, timestamp, user_cancelled_location='0,0'):
        self.set_pref('track_detection', 'last_selected_track_id', track_id)
        self.set_pref('track_detection', 'last_selected_track_timestamp', timestamp)
        self.set_pref('track_detection', 'user_cancelled_location', user_cancelled_location)
        self.save()

    @property
    def datastore_location(self):
        return os.path.join(self.data_dir, 'datastore.sq3')

    def save(self, *largs):
        '''
        Saves the current configuration
        '''
        Logger.info('UserPrefs: Saving preferences')
        with open(self.prefs_file, 'w+') as prefs_file:
            data = self.to_json()
            prefs_file.write(data)

    def set_config_defaults(self):
        '''
        Set defaults for preferences 
        '''
        # Base system preferences
        self.config.adddefaultsection('help')
        self.config.adddefaultsection('preferences')
        self.config.setdefault('preferences', 'distance_units', 'miles')
        self.config.setdefault('preferences', 'temperature_units', 'Fahrenheit')
        self.config.setdefault('preferences', 'show_laptimes', 1)
        self.config.setdefault('preferences', 'startup_screen', 'Home Page')
        default_user_files_dir = self.user_files_dir
        self.config.setdefault('preferences', 'config_file_dir', default_user_files_dir)
        self.config.setdefault('preferences', 'export_file_dir', default_user_files_dir)
        self.config.setdefault('preferences', 'firmware_dir', default_user_files_dir)
        self.config.setdefault('preferences', 'import_datalog_dir', default_user_files_dir)
        self.config.setdefault('preferences', 'send_telemetry', '0')
        self.config.setdefault('preferences', 'record_session', '1')
        self.config.setdefault('preferences', 'global_help', True)

        # Connection type for mobile
        if is_mobile_platform():
            if is_android():
                self.config.setdefault('preferences', 'conn_type', 'Bluetooth')
            elif is_ios():
                self.config.setdefault('preferences', 'conn_type', 'WiFi')
        else:
            self.config.setdefault('preferences', 'conn_type', 'Serial')

        # Dashboard preferences
        self.config.adddefaultsection('dashboard_preferences')
        self.config.setdefault('dashboard_preferences', 'last_dash_screen', '5x_gauge_view')
        self.config.setdefault('dashboard_preferences', 'pitstoptimer_enabled', 1)
        self.config.setdefault('dashboard_preferences', 'pitstoptimer_trigger_speed', 5)
        self.config.setdefault('dashboard_preferences', 'pitstoptimer_alert_speed', 25)
        self.config.setdefault('dashboard_preferences', 'pitstoptimer_exit_speed', 55)

        # Track detection pref
        self.config.adddefaultsection('track_detection')
        self.config.setdefault('track_detection', 'last_selected_track_id', 0)
        self.config.setdefault('track_detection', 'last_selected_track_timestamp', 0)
        self.config.setdefault('track_detection', 'user_cancelled_location', '0,0')

        self.config.adddefaultsection('analysis_preferences')
        self.config.setdefault('analysis_preferences', 'selected_sessions_laps', '{"sessions":{}}')
        self.config.setdefault('analysis_preferences', 'selected_analysis_channels', ','.join(UserPrefs.DEFAULT_ANALYSIS_CHANNELS))

        self.config.adddefaultsection('setup')
        self.config.setdefault('setup', 'setup_enabled', 1)

    def load(self):
        Logger.info('UserPrefs: Data Dir is: {}'.format(self.data_dir))
        self.config.read(os.path.join(self.data_dir, 'preferences.ini'))
        self.set_config_defaults()

        try:
            with open(self.prefs_file, 'r') as data:
                content = data.read()
                content_dict = json.loads(content)

                if content_dict.has_key("gauge_settings"):
                    for id, channel in content_dict["gauge_settings"].iteritems():
                        self._prefs_dict["gauge_settings"][id] = channel

                if content_dict.has_key('screens'):
                    self._prefs_dict['screens'] = content_dict['screens']

                if content_dict.has_key('alerts'):
                    for channel, alertrules in content_dict['alerts'].iteritems():
                        self._prefs_dict['alerts'][channel] = AlertRuleCollection.from_dict(alertrules)

        except Exception as e:
            Logger.error('Error loading preferences, using defaults. {}'.format(e))

    def init_pref_section(self, section):
        '''
        Initializes a preferences section with the specified name. 
        if the section already exists, there is no effect. 
        :param section the name of the preference section
        :type string
        '''
        self.config.adddefaultsection(section)

    def get_pref_bool(self, section, option, default=None):
        '''
        Retrieve a preferences value as a bool. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default bool
        :return bool preference value
        '''
        try:
            return self.config.getboolean(section, option)
        except (NoOptionError, ValueError):
            return default

    def get_pref_float(self, section, option, default=None):
        '''
        Retrieve a preferences value as a float. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default float
        :return float preference value
        '''
        try:
            return self.config.getfloat(section, option)
        except (NoOptionError, ValueError):
            return default

    def get_pref_int(self, section, option, default=None):
        '''
        Retrieve a preferences value as an int. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default user specified
        :return int preference value
        '''
        try:
            return self.config.getint(section, option)
        except (NoOptionError, ValueError):
            return default

    def get_pref(self, section, option, default=None):
        '''
        Retrieve a preferences value as a string. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default user specified
        :return string preference value
        '''
        try:
            return self.config.get(section, option)
        except (NoOptionError, ValueError):
            return default

    def get_pref_list(self, section, option, default=[]):
        """
        Retrieve a preferences value as a list. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default user specified
        :return list of string values
        """
        try:
            return self.config.get(section, option).split(',')
        except (NoOptionError, ValueError):
            return default

    def set_pref(self, section, option, value):
        '''
        Set a preference value
        :param section the configuration section for the preference
        :type string
        :param option the option for the section
        :type string
        :param value the preference value to set
        :type value user specified
        '''
        current_value = None
        try:
            current_value = self.config.get(section, option)
        except NoOptionError:
            pass
        self.config.set(section, option, value)
        self.config.write()
        if value != current_value:
            self.dispatch('on_pref_change', section, option, value)

    def set_pref_list(self, section, option, value):
        """
        Set a preference value by list
        :param section the configuration section for the preference
        :type string
        :param option the option for the section
        :type string
        :param value the preference value to set
        :type value list (list of strings)
        """
        try:
            self.set_pref(section, option, ','.join(value))
        except TypeError:
            Logger.error('UserPrefs: failed to set preference list for {}:{} - {}'.format(section, option, value))

    def to_json(self):
        '''
        Serialize preferences to json
        '''
        data = {'range_alerts': {}, 'gauge_settings':{}, 'screens': [], 'alerts': {}}

        for name, range_alert in self._prefs_dict["range_alerts"].iteritems():
            data["range_alerts"][name] = range_alert.to_dict()

        for id, channel in self._prefs_dict["gauge_settings"].iteritems():
            data["gauge_settings"][id] = channel

        for name, alertrules in self._prefs_dict['alerts'].iteritems():
            data['alerts'][name] = alertrules.to_dict()

        data['screens'] = self._prefs_dict['screens']

        return json.dumps(data, sort_keys=True, indent=2, separators=(',', ': '))
Exemple #6
0
class UserPrefs(EventDispatcher):
    '''
    A class to manage user preferences for the RaceCapture app
    '''
    _schedule_save = None
    _prefs_dict = {'range_alerts': {}, 'gauge_settings':{}}
    store = None
    prefs_file_name = 'prefs.json'
    prefs_file = None
    config = None
    data_dir = '.'
    user_files_dir = '.'

    def __init__(self, data_dir, user_files_dir, save_timeout=2, **kwargs):
        self.data_dir = data_dir
        self.user_files_dir = user_files_dir
        self.prefs_file = path.join(self.data_dir, self.prefs_file_name)
        self.load()
        self._schedule_save = Clock.create_trigger(self.save, save_timeout)

    def set_range_alert(self, key, range_alert):
        '''
        Sets a range alert with the specified key
        :param key the key for the range alert
        :type string
        :param range_alert the range alert
        :type object
        '''
        self._prefs_dict["range_alerts"][key] = range_alert
        self._schedule_save()

    def get_range_alert(self, key, default=None):
        '''
        Retrives a range alert for the specified key
        :param key the key for the range alert
        :type key string
        :param default the default value, optional
        :type default user specified
        :return the range alert, or the default value 
        '''
        return self._prefs_dict["range_alerts"].get(key, default)

    def set_gauge_config(self, gauge_id, channel):
        '''
        Stores a gauge configuration for the specified gauge_id
        :param gauge_id the key for the gauge
        :type gauge_id string
        :param channel the configuration for the channel
        :type channel object
        '''
        self._prefs_dict["gauge_settings"][gauge_id] = channel
        self._schedule_save()

    def get_gauge_config(self, gauge_id):
        '''
        Get the gauge configuration for the specified gauge_id
        :param gauge_id the key for the gauge
        :type string
        :return the gauge configuration
        '''
        return self._prefs_dict["gauge_settings"].get(gauge_id, False)

    def get_last_selected_track_id(self):
        return self.get_pref('track_detection', 'last_selected_track_id')

    def get_last_selected_track_timestamp(self):
        return self.get_pref_int('track_detection', 'last_selected_track_timestamp')

    def set_last_selected_track(self, track_id, timestamp):
        self.set_pref('track_detection', 'last_selected_track_id', track_id)
        self.set_pref('track_detection', 'last_selected_track_timestamp', timestamp)

    @property
    def datastore_location(self):
        return os.path.join(self.data_dir, 'datastore.sq3')

    def save(self, *largs):
        '''
        Saves the current configuration
        '''
        with open(self.prefs_file, 'w+') as prefs_file:
            data = self.to_json()
            prefs_file.write(data)

    def set_config_defaults(self):
        '''
        Set defaults for preferences 
        '''
        # Base system preferences
        self.config.adddefaultsection('help')
        self.config.adddefaultsection('preferences')
        self.config.setdefault('preferences', 'distance_units', 'miles')
        self.config.setdefault('preferences', 'temperature_units', 'Fahrenheit')
        self.config.setdefault('preferences', 'show_laptimes', 1)
        self.config.setdefault('preferences', 'startup_screen', 'Home Page')
        default_user_files_dir = self.user_files_dir
        self.config.setdefault('preferences', 'config_file_dir', default_user_files_dir)
        self.config.setdefault('preferences', 'firmware_dir', default_user_files_dir)
        self.config.setdefault('preferences', 'import_datalog_dir', default_user_files_dir)
        self.config.setdefault('preferences', 'first_time_setup', '1')
        self.config.setdefault('preferences', 'send_telemetry', '0')
        self.config.setdefault('preferences', 'record_session', '1')
        self.config.setdefault('preferences', 'last_dash_screen', 'gaugeView')
        self.config.setdefault('preferences', 'global_help', True)

        if platform == 'android':
            self.config.setdefault('preferences', 'conn_type', 'Bluetooth')

        # Dashboard preferences
        self.config.adddefaultsection('dashboard_preferences')
        self.config.setdefault('dashboard_preferences', 'pitstoptimer_enabled', 1)
        self.config.setdefault('dashboard_preferences', 'pitstoptimer_trigger_speed', 5)
        self.config.setdefault('dashboard_preferences', 'pitstoptimer_alert_speed', 25)
        self.config.setdefault('dashboard_preferences', 'pitstoptimer_exit_speed', 55)

        # Track detection pref
        self.config.adddefaultsection('track_detection')
        self.config.setdefault('track_detection', 'last_selected_track_id', 0)
        self.config.setdefault('track_detection', 'last_selected_track_timestamp', 0)

        self.config.adddefaultsection('analysis_preferences')
        self.config.setdefault('analysis_preferences', 'selected_sessions_laps', '{"sessions":{}}')

    def load(self):
        Logger.info('UserPrefs: Data Dir is: {}'.format(self.data_dir))
        self.config = ConfigParser()
        self.config.read(os.path.join(self.data_dir, 'preferences.ini'))
        self.set_config_defaults()

        self._prefs_dict = {'range_alerts': {}, 'gauge_settings':{}}

        try:
            with open(self.prefs_file, 'r') as data:
                content = data.read()
                content_dict = json.loads(content)

                if content_dict.has_key("range_alerts"):
                    for name, settings in content_dict["range_alerts"].iteritems():
                        self._prefs_dict["range_alerts"][name] = Range.from_dict(settings)

                if content_dict.has_key("gauge_settings"):
                    for id, channel in content_dict["gauge_settings"].iteritems():
                        self._prefs_dict["gauge_settings"][id] = channel

        except Exception:
            pass

    def get_pref_bool(self, section, option, default=None):
        '''
        Retrieve a preferences value as a bool. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default bool
        :return bool preference value
        '''
        try:
            return self.config.getboolean(section, option)
        except (NoOptionError, ValueError):
            return default

    def get_pref_float(self, section, option, default=None):
        '''
        Retrieve a preferences value as a float. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default float
        :return float preference value
        '''
        try:
            return self.config.getfloat(section, option)
        except (NoOptionError, ValueError):
            return default

    def get_pref_int(self, section, option, default=None):
        '''
        Retrieve a preferences value as an int. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default user specified
        :return int preference value
        '''
        try:
            return self.config.getint(section, option)
        except (NoOptionError, ValueError):
            return default

    def get_pref(self, section, option, default=None):
        '''
        Retrieve a preferences value as a string. 
        return default value if preference does not exist
        :param section the configuration section for the preference
        :type section string
        :param option the option for the section
        :type option string
        :param default
        :type default user specified
        :return string preference value
        '''
        try:
            return self.config.get(section, option)
        except (NoOptionError, ValueError):
            return default

    def set_pref(self, section, option, value):
        '''
        Set a preference value
        :param section the configuration section for the preference
        :type string
        :param option the option for the section
        :type string
        :param value the preference value to set
        :type value user specified
        '''
        self.config.set(section, option, value)
        self.config.write()

    def to_json(self):
        '''
        Serialize preferences to json
        '''
        data = {'range_alerts': {}, 'gauge_settings':{}}

        for name, range_alert in self._prefs_dict["range_alerts"].iteritems():
            data["range_alerts"][name] = range_alert.to_dict()

        for id, channel in self._prefs_dict["gauge_settings"].iteritems():
            data["gauge_settings"][id] = channel

        return json.dumps(data)
Exemple #7
0
class VKGroups(App, AuthorizationOnVK, GetAndSaveLoginPassword):
    '''Функционал программы.'''

    title = 'VKGroups'
    icon = 'icon.png'
    use_kivy_settings = False
    nav_drawer = ObjectProperty()
    theme_cls = ThemeManager()
    theme_cls.primary_palette = 'BlueGrey'
    lang = StringProperty('ru')

    def __init__(self, **kvargs):
        super(VKGroups, self).__init__(**kvargs)

        Window.bind(on_keyboard=self.events_program)

        self.POSSIBLE_FILES = \
            ['.png', '.jpg', '.jpeg', '.gif', '.zip', '.txt']
        self.DEVISE_ONLINE = {
            'mobile': 'desktop-mac',
            'computer': 'laptop',
            0: 'power'
        }

        self.PATTERN_WHOM_COMMENT = pattern_whom_comment
        self.PATTERN_REPLACE_LINK = pattern_replace_link

        self.window = Window
        self.config = ConfigParser()
        # Окно прогресса.
        self.load_dialog = ModalView(size_hint=(None, None),
                                     pos_hint={
                                         'x': 5.0 / Window.width,
                                         'y': 5.0 / Window.height
                                     },
                                     background_color=[0, 0, 0, .2],
                                     size=(dp(120), dp(50)),
                                     background=os.path.join(
                                         'data', 'images', 'decorator.png'),
                                     auto_dismiss=False)
        # Экземпляр для вывода списка плагинов пользователя.
        self.instance_list_user_plugins = ShowPlugins(self)
        # Файловый менеджер.
        self.window_file_manager = ModalView(
            size_hint=(1, 1),
            auto_dismiss=False,
            on_open=lambda x: self.load_dialog.dismiss())
        self.window_file_manager_open = False
        self.file_manager = FileManager(
            exit_manager=self.exit_manager,
            floating_button_color=self.theme_cls.primary_color)
        self.window_file_manager.add_widget(self.file_manager)

        self.current_screen_tab = None
        self.current_tab_manager = None
        self.name_press_tab = 'Home_page'
        self.file_manager_not_opening = True
        self.password_form = None
        self.box_posts = None
        self.attach_file = []
        self.attach_image = []
        self.box_for_attach_file = None
        self.box_for_attach_image = None
        self.group_info = None
        self.result_sending_post = None
        self.exit_interval = False
        self.flag_attach = ''
        self.window_user_groups = None
        self.window_language = None
        self.path_to_avatar = os.path.join(self.directory, 'data', 'images',
                                           'avatar.png')
        self.dict_language = ast.literal_eval(
            open(os.path.join(self.directory, 'data', 'locales',
                              'locales')).read())

    def get_application_config(self):
        return super(VKGroups, self).get_application_config(
            '{}/%(appname)s.ini'.format(self.directory))

    def build_config(self, config):
        '''Создаёт файл настроек приложения vkgroups.ini.'''

        config.adddefaultsection('General')
        config.setdefault('General', 'language', 'ru')
        config.setdefault('General', 'theme', 'default')
        config.setdefault('General', 'authorization', 0)
        config.setdefault('General', 'issues_in_group', 0)
        config.setdefault('General', 'count_issues', 20)
        config.setdefault('General', 'user_name', 'User')
        config.setdefault('General', 'last_group', '99411738')
        config.setdefault('General', 'regdata',
                          "{'login': None, 'password': None}")
        config.setdefault('General', 'last_screen', 'load screen')
        config.setdefault('General', 'last_path', '/')
        config.setdefault('General', 'show_dialog_on_download', 0)

    def set_value_from_config(self):
        '''Устанавливает значения переменных из файла настроек
        vkgroups.ini.'''

        self.config.read(os.path.join(self.directory, 'vkgroups.ini'))
        self.theme = self.config.get('General', 'theme')
        self.language = self.config.get('General', 'language')
        self.authorization = self.config.getint('General', 'authorization')
        self.last_path = self.config.get('General', 'last_path')
        self.last_screen = self.config.get('General', 'last_screen')
        self.regdata = \
            ast.literal_eval(self.config.get('General', 'regdata'))
        self.login = self.regdata['login']
        self.password = self.regdata['password']
        try:
            self.user_name = ast.literal_eval(
                self.config.get('General', 'user_name')).decode('utf-8')
        except ValueError:
            self.user_name = self.config.get('General', 'user_name')
        self.group_id = self.config.get('General', 'last_group')
        self.count_issues = self.config.getint('General', 'count_issues')
        self.issues_in_group = \
            self.config.getint('General', 'issues_in_group')
        self.show_dialog_on_download = \
            self.config.getint('General', 'show_dialog_on_download')

    def build(self):
        self.set_value_from_config()
        self.translation = Translation(self.language, 'kivyissues',
                                       '%s/data/locales' % self.directory)
        self.RELATION = {
            1: self.translation._('не женат/не замужем'),
            2: self.translation._('есть друг/подруга'),
            3: self.translation._('помолвлен'),
            4: self.translation._('женат/замужем'),
            5: self.translation._('всё сложно'),
            6: self.translation._('в активном поиске'),
            7: self.translation._('влюблён/влюблена'),
            8: self.translation._('в гражданском браке'),
            0: self.translation._('не указано')
        }
        self.message_about_files_mismatch = {
            'FILE': self.translation._('This file unsupported!'),
            'FOTO': self.translation._('This is not image!')
        }
        self.load_dialog.add_widget(
            MDLabel(text=self.translation._(' Загрузка...')))
        self.load_all_kv_files(self.directory + '/libs/uix/kv')
        self.screen = StartScreen()  # главный экран программы
        self.navigation_button = self.screen.ids.navigation_button
        self.previous = self.navigation_button.ids.previous
        self.manager = self.screen.ids.manager
        self.nav_drawer = self.screen.ids.nav_drawer
        self.current_screen_tab = self.navigation_button.ids.home_page

        if not self.login or not self.password:
            self.show_screen_registration()
        else:  # авторизация на сервере
            self._authorization_on_vk(self.login, self.password, self.group_id)

        Clock.schedule_interval(self.wait_info_for_home_page_group, 1)
        return self.screen

    def wait_info_for_home_page_group(self, interval):
        '''Ожидает получения данных от сервера после чего устанавливает
        значения переменных для экрана Previous.'''

        if self.group_info:
            self.previous.ids.group_title.source = \
                self.group_info['photo_200']
            self.previous.ids.group_name.text = \
                '[size=17][b]%s[/b][/size]\n[size=14]%s[/size]' % (
                    self.group_info['name'], self.group_info['status']
                )
            self.previous.ids.group_link.text = \
                '[ref={link}]{link}[/ref]'.format(
                    link='https://vk.com/%s' % self.group_info['screen_name']
                )
            self.previous.ids.group_people.text = \
                '%s %s' % (
                    self.translation._('Участники'),
                    self.group_info['members_count']
                )
            self.previous.ids.description.text = \
                self.group_info['description']
            self.nav_drawer.ids.user_name.text = \
                '[b]%s[/b]\n[size=12]online[/size]\n' % get_user_name()[0]

            Clock.unschedule(self.wait_info_for_home_page_group)
            Clock.schedule_once(
                lambda kwargs: self.show_form_for_messages(whom_name=''), 1)
            self.check_groups_user(name_group=self.group_info['name'],
                                   info_group=self.group_info['status'],
                                   logo_group=self.group_info['photo_200'])

        if os.path.exists('%s/data/state.txt' % self.directory):
            os.remove('%s/data/state.txt' % self.directory)

    def check_state_fields(self, id_instance, text_field):
        '''Сохраняет содержимое формы регистрации.'''

        if text_field == '':
            return

        if os.path.exists('%s/data/state.txt' % self.directory):
            data_state = ast.literal_eval(
                open('%s/data/state.txt' % self.directory).read())
        else:
            data_state = {}

        data_state[id_instance] = text_field

        # TODO: добавить шифрование данных.
        with open('%s/data/state.txt' % self.directory, 'w') as state_form:
            state_form.write(str(data_state))

    def check_groups_user(self, **kwargs):
        '''Проверяет и добавляет в список новые группы пользователя, которые он посещал.
        Данные имеют вид:
        {'Имя группы': ['Описание группы', 'ссылка на логотип группы', 'id группы,]}.'''

        file_groups_path = '%s/data/groups_user.ini' % self.directory
        if not os.path.exists(file_groups_path):
            with open(file_groups_path, 'w') as file_groups_user:
                file_groups_user.write('{}')

        groups_user = ast.literal_eval(
            open('%s/data/groups_user.ini' % self.directory,
                 encoding='utf-8').read())
        if not kwargs['name_group'] in groups_user:
            groups_user[kwargs['name_group']] = \
                [kwargs['info_group'], kwargs['logo_group'], self.group_id]
            with open(file_groups_path, 'w',
                      encoding='utf-8') as file_groups_user:
                file_groups_user.write(str(groups_user))

    def show_login_and_password(self, selection):
        '''
        Устанавливает свойства текстовых полей для ввода логина и пароля.

        :type selection: <class 'int'>

        '''

        if selection:
            self.password_form.ids.login.password = False
            self.password_form.ids.password.password = False
        else:
            self.password_form.ids.login.password = True
            self.password_form.ids.password.password = True

    def show_groups_user(self, *args):
        '''Выводит окно со списком групп пользователя.'''
        def callback_on_button_click(**kwargs):
            def on_press(text_button):
                # TODO: добавить группу в словарь groups.ini.
                field.dismiss()
                toast(str(text_button))

            field = input_dialog(
                title=kwargs['title'],
                hint_text='',
                text_button_ok=self.translation._('Добавить'),
                text_button_cancel=self.translation._('Отмена'),
                events_callback=on_press)

        def callback_on_item_click(name_item, mode):
            description_group, logo_group, id_group = groups_user[name_item]
            dialog(title=name_item,
                   text='%s\nID - %s' % (description_group, str(id_group)))

        if not self.window_user_groups:
            dict_groups_user = {}
            groups_user = ast.literal_eval(
                open('%s/data/groups_user.ini' % self.directory,
                     encoding='utf-8').read())

            for name_group in groups_user.keys():
                description_group, logo_group, id_group = groups_user[
                    name_group]
                dict_groups_user[name_group] = [description_group, logo_group]

            list_user_groups = ListUserGroups()
            _list = list_user_groups.ids.groups_list
            _list.events_callback = callback_on_item_click
            list_user_groups.ids.add_group.on_press = \
                lambda **kwargs: callback_on_button_click(
                    title=self.translation._('Введите ID группы:')
                )
            _list.two_list_custom_icon(dict_groups_user, IconItemAsync)

            self.window_user_groups = card(list_user_groups, size=(.85, .55))
        self.window_user_groups.open()

    def show_screen_registration(self, fail_registration=False):
        '''Окно с формой регистрации.'''

        if not self.password_form:
            self.password_form = \
                PasswordForm(callback=self.check_fields_login_password)
            self.password_form.ids.group_id.text = self.group_id

        self.screen.ids.load_screen.add_widget(self.password_form)

        # Если произошла ошибка регистрации, деактивируем спиннер и чистим
        # лейблы статуса авторизации.
        if fail_registration:
            self.screen.ids.load_screen.ids.spinner.active = False
            self.screen.ids.load_screen.ids.status.text = ''

    def show_screen_connection_failed(self, text_error):
        '''Выводит подпись о не активном соединении и кнопку для повторного
        подключения.'''

        self.screen.ids.load_screen.ids.spinner.active = False
        self.screen.ids.load_screen.ids.status.text = ''

        box = BoxLayout(orientation='vertical',
                        spacing=dp(10),
                        size_hint_y=None,
                        height=dp(100),
                        pos_hint={'center_y': .5})
        texts_errors = {
            'OAuth2 authorization error':
            self.translation._('Повторите попытку позже…'),
            'Failed to establish a new connection':
            self.translation._('Соединение Интернет отсутствует')
        }

        _text_error = self.translation._('Неизвестная ошибка…')
        for name_error in texts_errors.keys():
            if name_error in text_error:
                _text_error = texts_errors[name_error]

        box.add_widget(
            MDLabel(text=_text_error, halign='center', font_style='Subhead'))
        box.add_widget(
            MDFlatButton(text=self.translation._('Повторить попытку'),
                         theme_text_color='Custom',
                         pos_hint={'center_x': .5},
                         text_color=self.theme_cls.primary_color,
                         on_release=lambda x: self._authorization_on_vk(
                             self.login,
                             self.password,
                             self.group_id,
                             from_fail_screen=True)))
        self.screen.ids.load_screen.add_widget(box)

    @thread
    def send_post(self, text):
        self.result_sending_post, text_error = \
             create_issue(text, self.attach_file, self.attach_image)

    @thread
    def send_messages(self, text, user_id):
        self.result_sending_post, text_error = \
            send_message(
                user_id=user_id,
                files=self.attach_file,
                images=self.attach_image,
                text=text
            )

    @thread
    def send_comment(self, text, post_id, comment_id):
        self.result_sending_post, text_error = \
            create_comment(text, self.attach_file, self.attach_image, post_id, comment_id)

    def show_result_sending_posts(self, interval):
        def unschedule():
            Clock.unschedule(self.show_result_sending_posts)
            self.result_sending_post = None
            toast(message)
            self.clear_box_for_attach(remove_all=True)

        message = self.translation._('Sent!')
        if self.result_sending_post:
            # Обновляем списки постов/комментариев.
            if self.name_press_tab != 'Home_page':
                self.box_posts.update_posts()
            unschedule()
        elif self.result_sending_post is False:
            message = self.translation._('Error while sending!')
            unschedule()

    def callback_for_input_text(self, **kwargs):
        '''Вызывается при событиях из формы ввода текста.'''

        self.flag_attach = kwargs.get('flag')
        data = kwargs.get('kwargs')
        comment_id = int(data.get('comment_id', 0))
        post_id = int(data.get('post_id', 0))
        user_id = int(
            data.get('kwargs').get('user_id')) if 'kwargs' in data else 0
        whom_name = data.get('whom_name', '')
        text = whom_name + ', ' + self.form_for_messages.ids.text_form.text if whom_name != '' else self.form_for_messages.ids.text_form.text

        if self.flag_attach in ('FILE', 'FOTO'):
            self.show_manager(self.last_path, self.tap_on_file_in_filemanager)
        elif self.flag_attach == 'SEND':
            if self.manager.current == 'navigation button' and self.name_press_tab != 'Home_page' or self.manager.current == 'user info':
                self.form_for_messages.hide()

            if text.isspace() or text != '':
                if self.manager.current == 'navigation button' and self.name_press_tab == 'Wall_posts':
                    self.send_comment(text, post_id, comment_id)
                elif self.manager.current == 'user info':
                    self.send_messages(text, user_id)
                else:
                    self.send_post(text)

                self.form_for_messages.clear()
                Clock.schedule_interval(self.show_result_sending_posts, 0)

    def clear_box_for_attach(self, remove_all=False):
        self.attach_file = []
        self.attach_image = []

        if remove_all:
            if self.box_for_attach_image:
                self.form_for_messages.remove_widget(self.box_for_attach_image)
            if self.box_for_attach_file:
                self.form_for_messages.remove_widget(self.box_for_attach_file)

        self.box_for_attach_image = None
        self.box_for_attach_file = None

    def remove_attach(self, select_instance_attach):
        '''Удаляет превью файлов и изображений из формы для отправки сообщений.'''
        def _remove_attach(interval):
            parent_widget.remove_widget(instance_attach)

        if self.box_for_attach_file and select_instance_attach in self.box_for_attach_file.children:
            parent_widget = self.box_for_attach_file
        else:
            parent_widget = self.box_for_attach_image

        for instance_attach in parent_widget.children:
            if instance_attach == select_instance_attach:
                Clock.schedule_once(_remove_attach, .25)
                break

    def add_preview_attached_image(self, path_to_attach):
        '''Добавляет превью файлов в форму для отправки сообщений.'''

        if os.path.splitext(path_to_attach)[1] not in self.POSSIBLE_FILES[-2:]:
            if not self.box_for_attach_image:
                self.box_for_attach_image = BoxAttach(spacing=dp(5))
                self.form_for_messages.add_widget(self.box_for_attach_image)
            self.box_for_attach_image.add_widget(
                Icon(source=path_to_attach,
                     size_hint=(None, None),
                     size=(dp(60), dp(120)),
                     on_release=self.remove_attach))

    def add_preview_attached_file(self, path_to_attach):
        if not self.box_for_attach_file:
            self.box_for_attach_file = BoxAttachFile(spacing=dp(5))
            self.form_for_messages.add_widget(self.box_for_attach_file)
        attach = SingleIconItem(icon='file',
                                text=os.path.split(path_to_attach)[1],
                                events_callback=self.remove_attach)
        attach.ids._lbl_primary.font_style = 'Caption'
        self.box_for_attach_file.add_widget(attach)

    def write_last_path_manager(self, path_to_file_folder, path_to_file):
        self.config.set('General', 'last_path', path_to_file_folder)
        self.config.write()
        self.last_path = path_to_file_folder

    def tap_on_file_in_filemanager(self, path_to_file):
        self.window_file_manager.dismiss()
        self.window_file_manager_open = False
        path_to_file_folder, name_file = os.path.split(path_to_file)
        self.write_last_path_manager(path_to_file_folder, path_to_file)
        if os.path.splitext(name_file)[1] not in self.POSSIBLE_FILES:
            toast(self.message_about_files_mismatch[self.flag_attach])
        else:
            if self.flag_attach == 'FILE':
                self.attach_file.append(path_to_file)
                self.add_preview_attached_file(path_to_file)
            else:
                self.attach_image.append(path_to_file)
            self.add_preview_attached_image(path_to_file)

    def set_avatar(self, path_to_avatar):
        self.nav_drawer.ids.avatar.source = path_to_avatar
        self.nav_drawer.ids.avatar.reload()

    def choice_avatar_user(self):
        '''Выводит файловый менеджер для выбора аватара
        и устанавливает его в качестве аватара пользователя.'''
        def on_select(path_to_avatar):
            self.window_file_manager.dismiss()

            if os.path.splitext(path_to_avatar)[1] \
                    not in self.POSSIBLE_FILES[:3]:
                toast(self.translation._('This is not image!'))
            else:
                new_path_to_avatar = \
                    self.directory + '/data/images/avatar.png'
                create_previous_portrait(path_to_avatar, new_path_to_avatar)
                self.set_avatar(new_path_to_avatar)
                toast(self.translation._('Аватар изменён'))
                self.nav_drawer.state = 'open'

        self.show_manager(self.last_path, on_select)

    def show_plugins(self, *args):
        self.instance_list_user_plugins.show_plugins()

    def show_manager(self, directory, callback=None):
        ''''Выводит на экран файловый менеджер.'''

        if self.file_manager_not_opening:
            self.load_dialog.open()
            self.file_manager_not_opening = False

        self.window_file_manager_open = True
        Clock.schedule_once(self.window_file_manager.open, .2)
        if callback:
            self.file_manager.select_path = callback
        self.file_manager.show(directory)

    def show_posts(self, instance_tab=None):

        self.current_screen_tab.clear_widgets()
        if not self.box_posts:
            self.box_posts = BoxPosts(_app=self)
        self.box_posts.clear()
        self.box_posts.show_posts(increment=False)

    def events_program(self, instance, keyboard, keycode, text, modifiers):
        '''Вызывается при нажатии кнопки Меню или Back Key
        на мобильном устройстве.'''

        if keyboard in (1001, 27):
            if self.nav_drawer.state == 'open':
                self.nav_drawer.toggle_nav_drawer()
            self.back_screen(keyboard)
        elif keyboard in (282, 319):
            pass

        return True

    def back_screen(self, event=None):
        '''Менеджер экранов. Вызывается при нажатии Back Key
        и шеврона "Назад" в ToolBar.'''

        current_screen = self.manager.current
        if not self.window_file_manager_open:
            self.clear_box_for_attach()
        # Нажата BackKey.
        if event in (1001, 27):
            # Если информация с сервера о группе ещё не получена -
            # идёт авторизация либо данные в процессе загрузки.
            if not self.group_info:
                return
            if self.name_press_tab == 'Wall_posts' and self.form_for_messages.visible and \
                    not self.window_file_manager_open:
                self.form_for_messages.hide()
                return
            if self.window_file_manager_open:
                self.file_manager.back()
                return
            if current_screen == 'navigation button':
                if hasattr(self, 'previous_image'):
                    if self.previous_image.previous_open:
                        self.previous_image.dismiss()
                        return
                if self.box_posts and self.box_posts.show_comments:
                    self.box_posts.show_comments = False
                    self.show_posts()
                    return
                self.dialog_exit()
                return
        if current_screen == 'show license':
            self.manager.current = self.manager.screens[-1].name
        elif current_screen == 'user info':
            self.manager.current = 'navigation button'
        else:
            if not self.login or not self.password:
                self.dialog_exit()
                return
            self.manager.current = self.manager.previous()

    def dialog_exit(self):
        def check_interval_press(interval):
            self.exit_interval += interval
            if self.exit_interval > 5:
                self.exit_interval = False
                Clock.unschedule(check_interval_press)

        if self.exit_interval:
            sys.exit(0)

        Clock.schedule_interval(check_interval_press, 1)
        toast(self.translation._('Нажмите еще раз для выхода'))

    def show_form_for_messages(self, **kwargs):
        '''Выводит форму для ввода комментариев.'''

        self.form_for_messages = FormForMessages(
            callback=self.callback_for_input_text, kwargs=kwargs)

        self.form_for_messages.show(
            parent_instance=self.current_screen_tab if self.manager.current == 'navigation button' \
            else self.screen.ids.user_info.ids.float_layout
        )
        if kwargs.get('whom_name') == '':
            self.form_for_messages.visible = False

    def form_for_messages_hide(self):
        if self.form_for_messages.visible and not self.form_for_messages.ids.text_form.focus:
            self.form_for_messages.hide()

    def show_about(self, *args):
        self.nav_drawer.toggle_nav_drawer()
        self.screen.ids.load_screen.ids.status.text = \
            self.translation._(
                '[size=20][b]VKGroups[/b][/size]\n\n'
                '[b]Версия:[/b] {version}\n'
                '[b]Лицензия:[/b] MIT\n\n'
                '[size=20][b]Разработчики[/b][/size]\n\n'
                '[b]Backend:[/b] [ref=https://m.vk.com/fogapod]'
                '[color={link_color}]Евгений Ершов[/color][/ref]\n'
                '[b]Frontend:[/b] [ref=https://m.vk.com/heattheatr]'
                '[color={link_color}]Иванов Юрий[/color][/ref]\n\n'
                '[b]Исходный код:[/b] '
                '[ref=https://github.com/HeaTTheatR/VKGroups]'
                '[color={link_color}]GitHub[/color][/ref]').format(
                version=__version__,
                link_color=get_hex_from_color(self.theme_cls.primary_color)
            )
        self.screen.ids.load_screen.ids.spinner.active = False
        self.manager.current = 'load screen'
        self.screen.ids.action_bar.left_action_items = \
            [['chevron-left', lambda x: self.back_screen()]]

    def show_license(self, *args):
        self.screen.ids.show_license.ids.text_license.text = \
            self.translation._('%s') % open(
                '%s/license/license_en.txt' %
                    self.directory, encoding='utf-8').read()
        self.nav_drawer._toggle()
        self.manager.current = 'show license'
        self.screen.ids.action_bar.left_action_items = \
            [['chevron-left', lambda x: self.back_screen()]]
        self.screen.ids.action_bar.title = \
            self.translation._('MIT LICENSE')

    def exit_manager(self, *args):
        '''Закрывает окно файлового менеджера.'''

        self.window_file_manager.dismiss()
        self.window_file_manager_open = False

    def select_locale(self, *args):
        '''Выводит окно со списком имеющихся языковых локализаций для
        установки языка приложения.'''
        def select_locale(name_locale):
            '''Устанавливает выбранную локализацию.'''

            for locale in self.dict_language.keys():
                if name_locale == self.dict_language[locale]:
                    self.lang = locale
                    self.config.set('General', 'language', self.lang)
                    self.config.write()

        dict_info_locales = {}
        for locale in self.dict_language.keys():
            dict_info_locales[self.dict_language[locale]] = \
                ['locale', locale == self.lang]

        if not self.window_language:
            self.window_language = card(Lists(dict_items=dict_info_locales,
                                              events_callback=select_locale,
                                              flag='one_select_check'),
                                        size=(.85, .55))
        self.window_language.open()

    def set_chevron_back_screen(self):
        '''Устанавливает шеврон возврата к предыдущему экрану в ToolBar.'''

        self.screen.ids.action_bar.left_action_items = \
            [['chevron-left', lambda x: self.back_screen(self.manager.current)]]

    def download_progress_hide(self, instance_progress, value):
        '''Скрывает виджет прогресса загрузки файлов.'''

        instance_progress.dismiss()
        self.screen.ids.action_bar.right_action_items = \
            [['download', lambda x: self.download_progress_show(instance_progress)]]

    def download_progress_show(self, instance_progress):
        self.screen.ids.action_bar.right_action_items = []
        instance_progress.open()
        instance_progress.animation_progress_from_fade()

    def download_complete(self, result):
        self.screen.ids.action_bar.right_action_items = []
        if result == 'Done':
            toast(self.translation._('Загружено'))
        else:
            toast(self.translation._('Ошибка загрузки'))

    def tap_on_icon_user(self, user_id):
        '''Вызывается при тапе по иконке пользователя в списках постов/комментариев.
        Получает, устанавливает и выводит на экран информацию о выбранном пользователе.
        '''
        def unschedule(error=False):
            if error:
                toast(self.translation._('Данные не получены'))
            Clock.unschedule(wait_result)
            self.load_dialog.dismiss()

        def set_value_for_screen(instance_screen_user_info):
            '''Устанавливает значения подписей в экране информации о пользователе.'''

            instance_screen_user_info.ids.avatar.source = result['photo_200']
            instance_screen_user_info.ids.user_name.text = instance_screen_user_info.ids.user_name.text % (
                result['first_name'] + ' ' + result['last_name'],
                self.translation._('Последний раз был в сети в ') +
                time.strftime("%H:%M",
                              time.localtime(result['last_seen']['time'])))
            instance_screen_user_info.ids.label_user_status.text = \
                instance_screen_user_info.ids.label_user_status.text % result['status']
            instance_screen_user_info.ids.message_button.bind(
                on_release=lambda kwargs: self.show_form_for_messages(
                    kwargs={'user_id': user_id}))

            data = {
                'bdate': {
                    'result': result['bdate'] if 'bdate' in result else None,
                    'bad_result': self.translation._('скрыта'),
                    'label_instance':
                    instance_screen_user_info.ids.label_bdate,
                    'label_text': self.translation._('Дата рождения')
                },
                'city': {
                    'result':
                    result['city']['title'] if 'city' in result else None,
                    'bad_result': self.translation._('не указан'),
                    'label_instance': instance_screen_user_info.ids.label_city,
                    'label_text': self.translation._('Город')
                },
                'relation': {
                    'result':
                    self.RELATION[result['relation']]
                    if 'relation' in result else None,
                    'bad_result':
                    self.translation._('не указано'),
                    'label_instance':
                    instance_screen_user_info.ids.label_marital_status,
                    'label_text':
                    self.translation._('Семейное положение')
                }
            }

            for key in data:
                text_for_label = data[key]['result'] if data[key][
                    'result'] else data[key]['bad_result']
                data[key]['label_instance'].text = data[key][
                    'label_instance'].text % (data[key]['label_text'],
                                              text_for_label)
            unschedule()

        def wait_result(interval):
            '''Ожидает информации от сервера.'''

            if result:
                self.manager.current = 'user info'
                set_value_for_screen(self.screen.ids.user_info)

        if user_id == int(self.group_id):
            return
        result = None
        self.load_dialog.open()
        Clock.schedule_once(wait_result, 0)
        result, text_error = get_user_info(user_id=user_id)
        if text_error:
            unschedule(error=True)

    def load_all_kv_files(self, directory_kv_files):
        for kv_file in os.listdir(directory_kv_files):
            with open(os.path.join(directory_kv_files, kv_file),
                      encoding='utf-8') as kv:
                Builder.load_string(kv.read())

    def on_tab_press(self, instance_tab):
        '''Вызывается при выборе одного из пунктов BottomNavigation.'''

        self.clear_box_for_attach()
        self.name_press_tab = instance_tab.name
        self.current_screen_tab = instance_tab
        self.current_tab_manager = instance_tab.parent_widget.ids.tab_manager

        if self.current_tab_manager.current == self.name_press_tab:
            return

        # Вкладка 'Записи группы'.
        if self.name_press_tab == 'Wall_posts':
            self.show_posts(instance_tab)
            self.form_for_messages.hide()
        elif self.name_press_tab == 'Home_page':
            self.box_posts = None
            self.show_form_for_messages(whom_name='')

    def on_lang(self, instance, lang):
        self.translation.switch_lang(lang)

    def on_pause(self):
        '''Ставит приложение на 'паузу' при сворачивании его в трей.'''

        self.config.set('General', 'last_screen', self.manager.current)
        self.config.write()

        return True

    def on_start(self):
        '''Вызывается при открытии стартового экрана приложения.'''

        # Восстанавливаем форму ввода логина и пароля.
        if self.last_screen == 'load screen' and \
                not self.login and not self.password:
            if os.path.exists('%s/data/state.txt' % self.directory):
                data_state = \
                    ast.literal_eval(open('%s/data/state.txt' % self.directory).read())
                self.password_form.ids.login.text = \
                    data_state['login'] if 'login' in data_state else ''
                self.password_form.ids.password.text = \
                    data_state['password'] if 'password' in data_state else ''
                self.password_form.ids.group_id.text = data_state['group']
Exemple #8
0
class MainApp(App):
    def __init__(self, *args, **kwargs):
        super(MainApp, self).__init__(*args, **kwargs)
        self.logger = logging.getLogger('kivy.operator.app')
        if not os.path.isdir("/sdcard/operator"):
            os.makedirs("/sdcard/operator")
        self.map = None
        self.messaging = None
        self.xmpp_client = None
        self.user_location_markers = {}
        self._last_location_update = 0
        Window.bind(on_keyboard=self.on_back_btn)
        self.android_setflag()
        self.xmpp_config_ok = False
        self.start = True
        self.confirmation_popup = Popup()
        self.lock_btn_presses = []
        self.configuration = ConfigParser()
        self.configuration.read(CONFIG_PATH)
        self.check_config()

    def check_config(self):
        """
		Checks to see if the config has the required XMPP fields filled out accordingly.
		Then, it evaluates the config file to make sure that all fields exist, at least corresponding to the
		example config file.
		"""
        conf = self.configuration

        if conf.has_section('xmpp'):
            if all(
                    conf.has_option('xmpp', k) and conf.get('xmpp', k)
                    for k in MANDATORY_XMPP_OPTIONS):
                self.xmpp_config_ok = True

        def_conf = ConfigParser()
        def_conf.read(DEFAULT_CONFIG_PATH)

        for section in def_conf.sections():
            if conf.has_section(section):
                for option in def_conf.options(section):
                    if not conf.has_option(section, option) or conf.get(
                            section, option) is None:
                        conf.set(section, option,
                                 def_conf.get(section, option))
            else:
                conf.add_section(section)
                for option in def_conf.options(section):
                    conf.set(section, option, def_conf.get(section, option))

        self.configuration = conf
        self.configuration.write()

    def build(self):
        self.root = RootWidget()
        if self.xmpp_config_ok:
            self.xmpp_client = OperatorXMPPClient(
                sz_utils.parse_server(self.configuration.get('xmpp', 'server'),
                                      5222),
                self.configuration.get('xmpp', 'username'),
                self.configuration.get('xmpp', 'password'),
                self.configuration.get('xmpp', 'room'),
                self.configuration.getboolean('xmpp', 'filter'))
            self.xmpp_client.bind(
                on_user_location_update=self.on_user_location_update)
            self.xmpp_client.bind(on_message_receive=self.on_message_receive)
            self.xmpp_client.bind(on_muc_receive=self.on_muc_receive)
        else:
            self.logger.warning(
                "XMMP config invalid, disabling XMPP operations")

        self.map = self.root.ids.map_panel_widget.ids.map_widget
        self.messaging = self.root.ids.message_menu
        gps.configure(on_location=self.on_gps_location)
        gps.start()
        return self.root

    def on_back_btn(self, window, key, *args):
        """ To be called whenever user presses Back/Esc Key """
        # If user presses Back/Esc Key
        if key == 27:
            return self.root.on_back_btn()
        return False

    def build_config(self, config):
        # add default sections here
        default_sections = ('miscellaneous', 'xmpp')
        for section in default_sections:
            if not config.has_section:
                config.add_section(section)

        # load the custom configuration ini file
        custom_config = 'data/settings/config.ini'
        if os.path.isfile(custom_config):
            self.logger.info(
                'loading custom config: {0}'.format(custom_config))
            config.update_config(custom_config, overwrite=False)

    def build_settings(self, settings):
        settings.add_json_panel('XMPP', self.configuration,
                                'data/settings/xmpp.json')
        settings.add_json_panel('Map', self.configuration,
                                'data/settings/map.json')

    def on_message_receive(self, event, msg):
        self.messaging.on_message_receive(msg)

    def on_muc_receive(self, event, msg):
        self.messaging.on_muc_receive(msg)

    def on_gps_location(self, **kwargs):
        # kwargs on Galaxy S5 contain:
        #   altitude, bearing, lat, lon, speed
        if self.start:
            if self.xmpp_client:
                self.messaging.get_users()
            self.start = False
        if not ('lat' in kwargs and 'lon' in kwargs):
            return
        current_time = time.time()
        if current_time - self._last_location_update < self.configuration.getint(
                'miscellaneous', 'gps_update_freq'):
            return
        latitude = kwargs.pop('lat')
        longitude = kwargs.pop('lon')
        altitude = kwargs.pop('altitude', None)
        bearing = kwargs.pop('bearing', None)
        speed = kwargs.pop('speed', None)

        self.map.update_location((latitude, longitude), altitude, bearing,
                                 speed)
        if self.xmpp_client:
            self.xmpp_client.update_location((latitude, longitude), altitude,
                                             bearing, speed)
        self._last_location_update = current_time

    def get_users(self):
        return self.xmpp_client.get_users()

    def send_message(self, msg, user):
        self.xmpp_client.on_message_send(msg, user)

    def send_muc(self, msg, group):
        self.xmpp_client.on_muc_send(msg, group)

    def on_pause(self):
        return False

    def on_resume(self):
        pass

    def on_stop(self):
        self.map.save_marker_file()
        if self.xmpp_client:
            self.xmpp_client.shutdown()

    def on_user_location_update(self, _, info):
        if not self.map.is_ready:
            self.logger.warning('map is not ready for user marker')
            return
        user = info['user']
        if user in self.user_location_markers:
            self.user_location_markers[user].remove()
        if self.xmpp_client:
            user_mood = self.xmpp_client.user_moods.get(user, 'calm')
        icon_color = {
            'angry': 'red',
            'calm': 'yellow',
            'happy': 'green'
        }.get(user_mood, 'yellow')
        marker = self.map.create_marker(draggable=False,
                                        title=info['user'],
                                        position=info['location'],
                                        marker_color=icon_color)
        self.user_location_markers[user] = marker

    def toast_status(self):
        return self.configuration.getboolean('xmpp', 'toast_all')

    def xmpp_log(self, log_type, log):
        if log_type == 'info':
            self.xmpp_client.logger.info(log)

    def prompt_lock_screen(self):
        """
		Popup confirming with the user whether they want to lock the screen.
		"""
        confirmation_box = BoxLayout(orientation='vertical')
        confirmation_box.add_widget(
            Label(text='Do you want to lock the screen?'))
        box_int = BoxLayout(orientation='horizontal', spacing=50)
        affirm_button = Button(text='Yes')
        affirm_button.bind(on_release=lambda x: self.lock_screen())
        dismiss_button = Button(text='Cancel')
        dismiss_button.bind(
            on_release=lambda x: self.confirmation_popup.dismiss())
        box_int.add_widget(affirm_button)
        box_int.add_widget(dismiss_button)
        confirmation_box.add_widget(box_int)
        self.confirmation_popup = Popup(title='Confirmation',
                                        content=confirmation_box,
                                        size_hint=(.7, None),
                                        size=(500, 500),
                                        auto_dismiss=False)
        self.confirmation_popup.open()

    @run_on_ui_thread
    def lock_screen(self):
        """
		Lock the screen by going to a black layout and not allowing input. Will disable after 10 taps.
		"""
        self.confirmation_popup.dismiss()
        flag = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        PythonActivity.mActivity.getWindow().getDecorView(
        ).setSystemUiVisibility(flag)
        mb = True
        for child in self.root.walk():
            if hasattr(child, 'name'):
                if child.name == 'root' and mb:
                    self.removed = child
                    child.parent.remove_widget(child)
                    mb = False
        self.bl = BoxLayout(orientation='vertical')
        self.bl.add_widget(
            Button(size_hint=(1, 1),
                   background_color=[0, 0, 0, 1],
                   on_release=lambda x: self.lock_button()))
        self.root.add_widget(self.bl)

    def lock_button(self):
        """
		Registers clicks on the locked screen. Only counts clicks within 10 second gap.
		"""
        current_time = time.time()
        self.lock_btn_presses.append(current_time)
        while current_time - self.lock_btn_presses[
                0] > self.configuration.getint('miscellaneous',
                                               'lockout_timeout'):
            del self.lock_btn_presses[0]

        if len(self.lock_btn_presses) == self.configuration.getint(
                'miscellaneous', 'lockout_clicks'):
            self.root.remove_widget(self.bl)
            self.root.add_widget(self.removed)
            self.lock_btn_presses = []

    @run_on_ui_thread
    def android_setflag(self):
        PythonActivity.mActivity.getWindow().addFlags(
            Params.FLAG_KEEP_SCREEN_ON)

    def update_mood(self, mood):
        if self.xmpp_client:
            self.xmpp_client.update_mood(mood)