Как создать свое расширение файла python
Перейти к содержимому

Как создать свое расширение файла python

  • автор:

Разработка расширений на Python¶

Для разработки расширений можно использовать язык программирования Python. По сравнению с классическими расширениями, написанными на C++, их легче разрабатывать, понимать, поддерживать и распространять в силу динамической природы самого Python.

Расширения на Python перечисляются в Менеджере модулей QGIS наравне с расширениями на C++. Поиск расширений выполняется в следующих каталогах:

  • UNIX/Mac: ~/.qgis/python/plugins и (qgis_prefix)/share/qgis/python/plugins
  • Windows: ~/.qgis/python/plugins и (qgis_prefix)/python/plugins

В Windows домашний каталог (обозначенный выше как ~ ) обычно выглядит как C:\Documents and Settings\(user) . Вложенные каталоги в этих папках рассматриваются как пакеты Python, которые можно загружать в QGIS как расширения.

  1. Идея: Прежде всего нужна идея для нового расширения QGIS. Зачем это нужно? Какую задачу необходимо решить? Может, есть готовое расширения для решения этой задачи?
  2. Создание файлов: Подробнее этот шаг описан ниже. Точка входа ( __init.py__ ). Тело расширения ( plugin.py ). Форма QT-Designer ( form.ui ), со своим resources.qrc .
  3. Реализация: Пишем код в plugin.py
  4. Тестирование: Закройте и снова откройте QGIS, загрузите своё расширение. Проверьте, что всё работает как надо.
  5. Публикация: опубликуйте своё расширение в репозитории QGIS или настройте свой собственный репозиторий в качестве “арсенала” личного “ГИС вооружения”

Разработка расширения¶

С момента введения поддержки Python в QGIS появилось множество расширений — на странице Plugin Repositories можно найти некоторые из них. Исходный код этих расширений можно использовать, чтобы узнать больше о программировании с PyQGIS, а также для того, чтобы удостовериться, что разработка не дублируется. Готовы к созданию расширения, но отсутствует идея? На странице Python Plugin Ideas собрано много идей и пожеланий!

Создание необходимых файлов¶

Ниже показано содержимое каталога нашего демонстрационного расширения:

PYTHON_PLUGINS_PATH/ testplug/ __init__.py plugin.py metadata.txt resources.qrc resources.py form.ui form.py

Для чего используются файлы:

  • __init__.py = Точка входа расширения. Содержит общую информацию, версию расширения, его название и основной класс.
  • plugin.py = Основной код расширения. Содержит информацию обо всех действиях, доступных в расширении, а также основной код.
  • resources.qrc = XML-документ, созданный QT-Designer. Здесь хранятся относительные пути к ресурсам форм.
  • resources.py = Понятная Python версия вышеописанного файла.
  • form.ui = Интерфейс пользователя (GUI), созданный в QT-Designer.
  • form.py = Конвертированная в код Python версия вышеописанного файла.
  • metadata.txt = требуется в QGIS >= 1.8.0. Содержит общую информацию, версию расширения, его название и другие метаданые, используемые новым репозиторием расширений. Метаданным в metadata.txt отдается предпочтение перед методами из файла __init__.py . Если текстовый файл присутствует, именно он будет использоваться для получения этой информации. Начиная с QGIS 2.0 метаданные из __init__.py больше не будут использоваться и файл metadata.txt станет обязательным.

Здесь и вот здесь можно найти два способа автоматической генерации базовых файлов (скелета) типового Python расширения для QGIS. Кроме того, существует модуль Plugin Builder , который создает шаблон модуля прямо из QGIS и не требует соединения с Интернет. Это упростит работу и поможет быстрее начать разработку типового расширения.

Написание кода¶

__init__.py¶

Прежде всего, Менеджер модулей должен получить основные сведения о расширении, такие как его название, описание и т.д. Файл __init__.py именно то место, где должна быть эта информация:

def name(): return "My testing plugin" def description(): return "This plugin has no real use." def version(): return "Version 0.1" def qgisMinimumVersion(): return "1.0" def authorName(): return "Developer" def classFactory(iface): # загружаем класс TestPlugin из файла testplugin.py from testplugin import TestPlugin return TestPlugin(iface) 

В QGIS 1.9.90 модули могут быть помещены не только в меню Модули , но и в меню Растр , Вектор , База данных и Web . Поэтому было введено новое поле метаданных “category”. Это поле используется в качестве подсказки для пользователей и сообщает где (в каком меню) искать модуль. Допустимыми значениями для параметра “category” являются Vector, Raster, Database, Web и Layers. Например, если модуль должен быть доступен из меню Растр , добавьте в __init__.py следующие строки:

def category(): return "Raster" 

metadata.txt¶

Для QGIS >= 1.8 необходимо создать файл metadata.txt (см. также) Пример :file: ` metadata.txt’:

; the next section is mandatory [general] name=HelloWorld qgisMinimumVersion=1.8 description=This is a plugin for greeting the (going multiline) world category=Raster version=version 1.2 ; end of mandatory metadata ; start of optional metadata changelog=this is a very very very very very very long multiline changelog ; tags are in comma separated value format, spaces are allowed tags=wkt,raster,hello world ; these metadata can be empty ; in a future version of the web application it will ; be probably possible to create a project on redmine ; if they are not filled homepage=http://www.itopen.it tracker=http://bugs.itopen.it repository=http://www.itopen.it/repo icon=icon.png ; experimental flag experimental=True ; deprecated flag (applies to the whole plugin and not only to the uploaded version) deprecated=False

plugin.py¶

Стоит сказать несколько слов о функции classFactory() , которая вызывается когда расширение загружается в QGIS. Она получает ссылку на экземпляр класса QgisInterface и должна вернуть экземпляр класса вашего расширения — в нашем случае этот класс называется«TestPlugin«. Ниже показано он должен выглядеть (например, testplugin.py ):

from PyQt4.QtCore import * from PyQt4.QtGui import * from qgis.core import * # загружаем ресурсы Qt из файла resouces.py import resources class TestPlugin: def __init__(self, iface): # сохраняем ссылку на интерфейс QGIS self.iface = iface def initGui(self): # создаем действия для запуска расширения или его настройки self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", self.iface.mainWindow()) self.action.setWhatsThis("Configuration for test plugin") self.action.setStatusTip("This is status tip") QObject.connect(self.action, SIGNAL("triggered()"), self.run) # добавляем кнопку на панель и пункт в меню self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu("&Test plugins", self.action) # подключаемся к сигналу renderComplete, который посылается по завершению отрисовки карты QObject.connect(self.iface.mapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest) def unload(self): # удаляем пункт меню и кнопку на панели self.iface.removePluginMenu("&Test plugins",self.action) self.iface.removeToolBarIcon(self.action) # отключаемся от сигнала карты QObject.disconnect(self.iface.MapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest) def run(self): # создаем и показываем диалог настройки или выполняем что-то еще print "TestPlugin: run called!" def renderTest(self, painter): # рисуем на карте, используя painter print "TestPlugin: renderTest called!" 

Если используется QGIS 1.9.90 или старше и необходимо разместить модуль в одном из новых меню ( Растр , Вектор , База данных или Web ), нужно модифицировать код функций initGui() и unload() . Так как эти новые пункты меню доступны только в QGIS 1.9.90, прежде всего необходимо проверить, что используемая версия QGIS имеет все необходимые функции. Если новые пункты меню доступны, мы можем разместить модуль в нужном месте, в противном случае будем использовать меню Модули как и раньше. Вот пример для меню Растр :

def initGui(self): # создаем действия для запуска расширения или его настройки self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", self.iface.mainWindow()) self.action.setWhatsThis("Configuration for test plugin") self.action.setStatusTip("This is status tip") QObject.connect(self.action, SIGNAL("triggered()"), self.run) # проверяем, доступно ли меню Растр if hasattr(self.iface, "addPluginToRasterMenu"): # меню Растр и одноименная панель доступны self.iface.addRasterToolBarIcon(self.action) self.iface.addPluginToRasterMenu("&Test plugins", self.action) else: # меню Растр отсутствует, размещаем модуль в меню Модули, как и раньше self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu("&Test plugins", self.action) # одключаемся к сигналу renderComplete, который посылается по завершению отрисовки карты QObject.connect(self.iface.mapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest) def unload(self): # проверям доступно ли меню Растр и удаляем наши кнопки из соответствующего # меню и панели if hasattr(self.iface, "addPluginToRasterMenu"): self.iface.removePluginRasterMenu("&Test plugins",self.action) self.iface.removeRasterToolBarIcon(self.action) else: self.iface.removePluginMenu("&Test plugins",self.action) self.iface.removeToolBarIcon(self.action) # отключаемся от сигнала карты QObject.disconnect(self.iface.MapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest) 

Полный список методов, которые можно использовать для размещения модуля в новых меню и на новых панелях инструментов доступен в описании API.

В расширении обязательно должны присутствовать функции initGui() и unload() . Эти функции вызываются когда расширение загружается и выгружается.

Файл ресурсов¶

Как видно в примере выше, в initGui() мы использовали иконку из файла ресурсов (в нашем случае он называется resources.qrc ):

  icon.png  

Хорошим тоном считается использование префикса, это позволит избежать конфликтов с другими расширениями или с частями QGIS. Если префикс не задан, можно получить не те ресурсы, которые нужны. Теперь сгенерируем файл ресурсов на Python. Это делается командой pyrcc4:

pyrcc4 -o resources.py resources.qrc

Вот и все. ничего сложного 🙂 Если всё сделано правильно, то расширение должно отобразиться в Менеджере модулей и загружаться в QGIS без ошибок. После его загрузки на панели появится кнопка, а в меню — новый пункт, нажатие на которые приведет к выводу сообщения на терминал.

При работе над реальным расширением удобно вести разработку в другом (рабочем) каталоге и создать makefile, который будет генерировать файлы интерфейса и ресурсов, а также выполнять копирование расширения в каталог QGIS.

Документация¶

Этот способ создания документации требует наличия Qgis версии 1.5.

Документацию к расширению можно готовить в виде файлов HTML. Модуль qgis.utils предоставляет функцию showPluginHelp() , которая откроет файл справки в браузере, точно так же как другие файлы справки QGIS.

Функция showPluginHelp`() ищет файлы справки в том же каталоге, где находится вызвавший её модуль. Она по очереди будет искать файлы index-ll_cc.html , index-ll.html , index-en.html , index-en_us.html и index.html , и отобразит первый найденный. Здесь ll_cc — язык интерфейса QGIS. Это позволяет включать в состав расширения документацию на разных языках.

Кроме того, функция showPluginHelp() может принимать параметр packageName, идентифицирующий расширение, справку которого нужно отобразить; filename, который используется для переопределения имени файла с документацией; и section, для передачи имени якоря (закладки) в документе, на который браузер должен перейти.

Фрагменты кода¶

Здесь собраны фрагменты кода, полезные при разработке расширений.

Как вызвать метод по нажатию комбинации клавиш¶

Добавьте в initGui() :

self.keyAction = QAction("Test Plugin", self.iface.mainWindow()) self.iface.registerMainWindowAction(self.keyAction, "F7") # action1 is triggered by the F7 key self.iface.addPluginToMenu("&Test plugins", self.keyAction) QObject.connect(self.keyAction, SIGNAL("triggered()"),self.keyActionF7) 
self.iface.unregisterMainWindowAction(self.keyAction) 

Метод, вызываемый по нажатию F7:

def keyActionF7(self): QMessageBox.information(self.iface.mainWindow(),"Ok", "You pressed F7") 

Как управлять видимостью слоя (временное решение)¶

Примечание: в QGIS 1.5 появился класс QgsLegendInterface , позволяющий управлять списком слоёв легенды.

Так как в настоящее время методы прямого доступа к слоям легенды отсутствуют, в качестве временно решения для управления видимостью слоёв можно использовать решение на основе изменения прозрачности слоя:

def toggleLayer(self, lyrNr): lyr = self.iface.mapCanvas().layer(lyrNr) if lyr: cTran = lyr.getTransparency() lyr.setTransparency(0 if cTran > 100 else 255) self.iface.mapCanvas().refresh() 

Метод принимает номер слоя в качестве параметры (0 соответствует самому верхнему) и вызывается так:

self.toggleLayer(3) 

Как получить доступ к таблице атрибутов выделенных объектов¶

def changeValue(self, value): layer = self.iface.activeLayer() if(layer): nF = layer.selectedFeatureCount() if (nF > 0): layer.startEditing() ob = layer.selectedFeaturesIds() b = QVariant(value) if (nF > 1): for i in ob: layer.changeAttributeValue(int(i),1,b) # 1 соответствует второй колонке else: layer.changeAttributeValue(int(ob[0]),1,b) # 1 соответствует второй колонке layer.commitChanges() else: QMessageBox.critical(self.iface.mainWindow(),"Error", "Please select at least one feature from current layer") else: QMessageBox.critical(self.iface.mainWindow(),"Error","Please select a layer")

Метод принимает один параметр (новое значения атрибута выделенного объекта(ов)) и вызывается как:

self.changeValue(50) 

Как выполнять отладку при помощи PDB¶

Сначала добавьте следующий код в место, которое будет отлаживаться:

# для отладки используем pdb import pdb # устанавливаем точку останова pyqtRemoveInputHook() pdb.set_trace() 

Затем запускаем QGIS из командной строки.

$ ./Qgis

$ /Applications/Qgis.app/Contents/MacOS/Qgis

Когда приложение достигнет точки останова, консоль станет доступной и можно будет вводить команды!

Тестирование¶

Публикация расширения¶

Если после создания расширения вы решите, что оно может быть полезно и другим пользователям — не бойтесь загрузить его в репозиторий QGIS plugin repository. На этой же странице можно найти инструкции по подготовке пакета, следование которым избавит от проблем с установкой расширения через Установщик модулей. В случае, когда нужно настроить собственный репозиторий, создайте простой XML документ, описывающий все расширения и их метаданные. Пример файла можно найти на странице Python plugin repositories.

Примечание: настройка IDE в Windows¶

При использовании Linux разработка расширений не требует дополнительных настроек. Но в при использовании Windows необходимо убедиться, что и QGIS, и интерпретатор используют одни и те же переменные окружения и библиотеки. Наиболее простой и быстрый способ сделать это — модифицировать командный файл для запуска QGIS.

Если используется установщик OSGeo4W, командный файл можно найти в каталоге bin папки, куда выполнена установка OSGeo4W. Ищите что-то похожее на C:\OSGeo4W\bin\qgis-unstable.bat .

Далее будет описана настройка Pyscripter IDE. Настройка других сред разработки может несколько отличаться:

  • Сделайте копию qgis-unstable.bat и переименуйте её в pyscripter.bat.
  • Откройте это файл в редакторе. Удалите последнюю строку, которая отвечает за запуск QGIS.
  • Добавьте строку для запуска pyscripter с параметром, указывающим на используемую версию Python. QGIS 1.5 использует Python 2.5.
  • Добавьте еще один аргумент, указывающий на каталог, где pyscripter должен искать библиотеки Python, используемые qgis. Обычно это каталог bin папки, куда установлен OSGeo4W:

@echo off SET OSGEO4W_ROOT=C:\OSGeo4W call "%OSGEO4W_ROOT%"\bin\o4w_env.bat call "%OSGEO4W_ROOT%"\bin\gdal16.bat @echo off path %PATH%;%GISBASE%\bin start C:\pyscripter\pyscripter.exe --python25 --pythondllpath=C:\OSGeo4W\bin

Теперь при запуске этого командного файла установятся необходимые переменные окружения и будет запущен pyscripter.

Как организовать файлы по расширению в Python

Как организовать файлы по расширению в Python

Организация файлов на компьютере может быть утомительной задачей, особенно когда это делается вручную. Некоторые папки (например, папка «Загрузки») на компьютере часто забиты так, что мы не всегда можем найти то что нам нужно. В данной статье Вы узнаете как с помощью языка Python и его встроенных средств организовать размещение файлов по папкам в соответствии с их расширениями.

И так, давайте начнем. Как мы уже сказали выше, нам не понадобится для установки ничего, кроме стандартной библиотеки Python 3. Импортируем модули:

import os
import glob
import shutil

Мы будем использовать функцию shutil.move() из модуля shutil для перемещения файлов, и нам также понадобится модуль globe, чтобы получить список совпадающих файлов с помощью регулярного выражения. Мы также используем модуль os для обработки имен файлов.

Поскольку мы собираемся упорядочивать файлы по расширению, имеет смысл размещать файлы одного типа (например, mp3, wav) в одной папке (например, «аудио»). Ниже словарь Python сопоставляет каждое расширение с типом.:

extensions = «jpg»: «изображения»,
«png»: «изображения»,
«ico»: «изображения»,
«gif»: «изображения»,
«svg»: «изображения»,
«sql»: «SQL_»,
«exe»: «Программы»,
«msi»: «Программы»,
«pdf»: «pdf»,
«xlsx»: «Эксель»,
«csv»: «Эксель»,
«rar»: «Архивы»,
«zip»: «Архивы»,
«gz»: «Архивы»,
«tar»: «Архивы»,
«docx»: «ВОРД»,
«txt»: «Текст»,
«py»: «python»,
«pptx»: «powerpoint»,
«ppt»: «powerpoint»,
«mp3»: «аудио»,
«wav»: «аудио»,
«mp4»: «видео»,
«m3u8»: «видео»,
«webm»: «видео»,
«ts»: «видео»,
«json»: «json»,
«css»: «web»,
«js»: «web»,
«html»: «web»,
«apk»: «apk»,
«sqlite3»: «sqlite3»,
>

Так, файлы mp4 и m3u8 будут помещены в папку видео и так далее. Данный список не окончателен и может быть расширен.

Перейдем к следующему фрагменту кода.

# путь к целевой папке
path = r»С:\Downloads»
verbose = 1
for extension, folder_name in extensions.items():
# найдем все файлы подходящие по расширению
files = glob.glob(os.path.join(path, f»*.»))
print(f»[*] Найдено файлов с расширением»)
if not os.path.isdir(os.path.join(path, folder_name)) and files:
# создание новой директории
print(f»[+] Создание папки «)
os.mkdir(os.path.join(path, folder_name))
for file in files:
# Для каждого файла с соответствующим расширением помещаем его в нужную папку
basename = os.path.basename(file)
dst = os.path.join(path, folder_name, basename)
if verbose:
print(f»[*] перемещение в «)
shutil.move(file, dst)

Мы перебираем наш словарь, который сопоставляет каждое расширение с типом файла, получаем список файлов каждого расширения и перемещаем их в соответствующую папку. Мы также создаем папку с помощью функции os.mkdir(), если она ранее не существовала.

Переменная path — это путь к папке, которую вы хотите упорядочить, вы можете использовать модули sys или argparse для передачи пути к папке через cmd.

»’
вывод:
[+] Найдено 0 файлов с расширением .jpg
[+] Найдено 2 файлов с расширением .png
[+] Создаем папки изображения
[+] Найдено 0 файлов с расширением .ico
[+] Найдено 0 файлов с расширением .gif
»’

Изображение

Если теперь понадобится найти или удалить нужный файл в какой либо папке, то Python3 и его стандартная библиотека существенно упростят нашу задачу.

Создано 14.12.2021 08:47:08

  • Михаил Русаков
  • Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

    Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
    Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

    Если Вы не хотите пропустить новые материалы на сайте,
    то Вы можете подписаться на обновления: Подписаться на обновления

    Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

    Порекомендуйте эту статью друзьям:

    Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

    1. Кнопка:
      Она выглядит вот так:
    2. Текстовая ссылка:
      Она выглядит вот так: Как создать свой сайт
    3. BB-код ссылки для форумов (например, можете поставить её в подписи):

    Комментарии ( 0 ):

    Для добавления комментариев надо войти в систему.
    Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.

    Copyright © 2010-2024 Русаков Михаил Юрьевич. Все права защищены.

    Путь, имя и расширение файла

    Достаточно часто возникают ситуации, когда у нас есть полное имя файла, а требуется узнать его расширение. Или добавить нужное расширение, когда не известно, ввел его пользователь или нет. Иногда у нас есть относительный путь до файла, а требуется узнать абсолютный. Про основные методы работы с именем файла и будет эта статья.

    Абсолютный путь к файлу

    Для того чтобы узнать в Python абсолютный путь к файлу, потребуется воспользоваться библиотекой os. Её подключаем с помощью команды import os. В классе path есть метод abspath. Вот пример использования.

    import os p = os.path.abspath('file.txt ') print(p) C:\python3\file.txt

    Так же можно воспользоваться и стандартной библиотекой pathlib. Она вошла в состав основных библиотек, начиная с версии Python 3.4. До этого надо было ее инсталлировать с помощью команды pip install pathlib. Она предназначена для работы с путями файловой системы в разных ОС и отлично подойдет для решения данной задачи.

    import pathlib p = pathlib.Path('file.txt ') print(p) C:\python3\file.txt

    Имя файла

    Чтобы узнать имя файла из полной строки с путем, воспользуемся методом basename модуля os.

    import os name = os.path.basename(r'C:\python3\file.txt ') print(name) file.txt

    Здесь перед строкой вставил r, чтобы подавить возможное возникновение служебных символов. Например, в данном случае если не указать r, то \f считалось бы символом перевода страницы.

    Без расширения

    Теперь разберемся, как в Python узнать имя файла без расширения. Воспользуемся методом splittext. В этот раз для примера возьмем файл с двойным расширением, чтобы проверить, как будут в этой ситуации работать стандартны функции.

    from os import path full_name = path.basename(r'C:\python3\file.tar.gz ') name = path.splitext(full_name)[0] print(name) file.tar

    Видно, что последнее расширение архиватора gz было отброшено, в то время как расширение несжатого архива tar осталось в имени.

    Если же нам нужно только имя, то можно отбросить все символы полученной строки, которые идут после первой точки. Символ точки тоже отбросим.

    Дополним предыдущий пример следующим кодом:

    index = name.index('.') print(name[:index]) file

    Расширение файла

    В Python получить расширение файла можно аналогичным образом с помощью той же функции splitext. Она возвращает кортеж. Первый элемент кортежа имя, а второй – расширение. В данном случае нам нужен второй элемент. Индекс второго элемента равен единице, так как отсчет их идет от нуля.

    from os import path full_name = path.basename(r'C:\python3\file.tar.gz ') name = path.splitext(full_name)[1] print(name) .gz

    Аналогично можно воспользоваться библиотекой pathlib. Воспользуемся методом suffix.

    from pathlib import Path print(Path(r'C:\python3\file.tar.gz ').suffix) .gz

    Но в нашем случае два расширения. Их можно узнать с помощью функции suffixes. Она возвращает список, элементами которого и будут расширения. Ниже приведен пример получения списка расширений.

    from pathlib import Path print(Path(r'C:\python3\file.tar.gz ').suffixes) ['.tar', '.gz ']

    Для того, чтобы получить имя файла или расширение из полного пути или для получения абсолютного пути к файлу используйте библиотеки os и pathlib. Лучше воспользоваться готовым решением из стандартой библиотеками, чем писать свое решение.

    Расширение файла PYTHON

    Расширение файла PYTHON имеет один тип (-ов) файла (-ов) и связано с один различными программными обеспечениями, но главным образом с Python, разработанным Python Software Foundation. Часто они представлены в формате Python Script File. Чаще всего файлы PYTHON классифицируют, как Developer Files.

    Расширение файла PYTHON поддерживается Windows, Mac и Linux. Данные типы файлов можно найти в основном на настольных компьютерах и некоторых мобильных устройствах. Рейтинг популярности данных файлов составляет «Низкий» и они обычно не используются.

    Подробную информацию о файлах PYTHON и программах, с помощью которых они открываются, см. далее. Кроме того, далее также представлены сведения о простых способах устранения неполадок, которые помогут вам открыть файл PYTHON.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *