С Новым годом! Форум программистов, компьютерный форум, киберфорум
iamvic
Войти
Регистрация
Восстановить пароль
Путевые заметки в процессе познания Python и PyQt/PySide.
Помни - только тег CODE не портит код добавлением пробела в начало пустой строки.
Оценить эту запись

К вопросу о глобальных переменных и инкапсуляции в приложениях PyQt.

Запись от iamvic размещена 14.11.2021 в 11:48
Обновил(-а) iamvic 15.11.2021 в 19:22 (поправил форматирование текста)
Метки pyqt5, python, python 3, qt5

Как обычно, ничего нового не скажу (и не покажу ), а просто, пока не забылось, запишу в Памятку свои слегка причёсанные мысли по поводу, пересекающемуся с тем, что написано здесь https://www.cyberforum.ru/blog... g7326.html. Вот после прочтения этой статьи ни с того, ни с сего и озадачился я вопросом:

А существуют ли в Qt/PyQt "глобальные переменные" достаточно близкие по смыслу к тому, который вкладывает в это понятие уважаемый Viktorrus?

Не такие, как глобальные константы, перечисленные в пространстве имён Qt, а действительно настоящие глобальные переменные. Такие, чтобы в любой точке выполнения программы их можно было создать, получить или изменить их состояние или значение и, наконец, удалить. Или убедиться, что переменная уже существует или её ещё нет.

И как-то долго подспудно всё свербило, что я уже делаю нечто подобное в своих поделках. Что-то такое в одном месте создаю, открываю в другом, пользую повсеместно, а закрываю только на выходе. И всё это делаю "по имени"... Но это же QSqlDatabase!!!
Кликните здесь для просмотра всего текста
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    # соединение по умолчанию создаю в __main__
    db = QSqlDatabase.addDatabase('QPSQL')
    ...
 
    # открываю его в диалоге из модуля login_win.py
    if QSqlDatabase.database('qt_sql_default_connection').open():
    ...
 
    # пользую повсеместно в разных виджетах из соответствующих модулей
    if QSqlDatabase.database('qt_sql_default_connection').isOpen():
        model.setQuery(
            '<текст запроса>',
            QSqlDatabase.database('qt_sql_default_connection')
            )
    ...
 
    # закрываю его при выборе пункта меню в главном окне из модуля main_win.py
    QSqlDatabase.database('qt_sql_default_connection').close()
    ...

И если требуются дополнительные соединения, то поступаю точно также - создаю их по имени и получаю для работы тоже по имени, используя соответствующие Static Public Members класса QSqlDatabase. Ну и чем это отличается от "глобальной переменной"? Только механизмом доступа - не напрямую, а с помощью запросов "по имени", хотя, наверное, правильнее сказать "по псевдонимам".

Cъобезьяничать это решение из QSqlDatabase оказалось до неприличия просто. У меня получился вот такой минималистичный вариант, который хранит так называемые "глобальные переменные" в свойстве класса, а доступ к ним обеспечивает методами класса, подобными Static Public Members. Наверняка что-то подобное где-то уже предлагалось, но что получилось, то получилось. Пусть будет...

Файл global_vars.py:
Кликните здесь для просмотра всего текста
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/python3
# -*- coding: utf-8 -*-
 
class Pseudo_global_vars(object):
    '''Реализация глобальных переменных с доступом по псевдонимам.
    '''
    ___vars = {}
 
    def set(pseudo, value):
        '''Переменной с псевдонимом pseudo присваивается значение value.
        Если переменная с указанным псевдонимом отсутствует, то она создаётся.
        '''
        Pseudo_global_vars.___vars[pseudo] = value
        return None
 
    def get(pseudo):
        '''Возвращает значение, ранее присвоенное переменной с псевдонимом pseudo.
        Если переменная с указанным псевдонимом отсутствует, то возвращается None.
        '''
        if Pseudo_global_vars.contains(pseudo):
            return Pseudo_global_vars.___vars[pseudo]
        else:
            return None
 
    def drop(pseudo):
        '''Удаляет переменную с псевдонимом pseudo.
        '''
        if Pseudo_global_vars.contains(pseudo):
            Pseudo_global_vars.___vars.pop(pseudo)
        return True
 
    def contains(pseudo):
        '''Возвращает True, если переменная с псевдонимом pseudo существует,
        иначе - False.
        '''
        return pseudo in Pseudo_global_vars.names()
 
    def names():
        '''Возвращает список псевдонимов существующих переменных.
        '''
        return list(Pseudo_global_vars.___vars.keys())


Сюда уже можно накрутить чего попало - и журналирование "кто чего создавал, менял, удалял", и защиту какую-нибудь сочинить в стиле "не ты создавал - не тебе менять и удалять" и так далее.

Простейший пример того, как всё это работает, представлен ниже. Там вроде бы всё понятно и без комментариев.

Файл main.py:
Кликните здесь для просмотра всего текста
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/python3
# -*- coding: utf-8 -*-
 
import sys
from global_vars import Pseudo_global_vars
from my_object import My_object
 
if __name__ == '__main__':
    print('0: __main__ --> global vars list =',
          Pseudo_global_vars.names())
    print('0: __main__ --> is global_kg found? =',
          Pseudo_global_vars.contains('global_kg'))
    print('0: __main__ --> value of global_kg =',
          Pseudo_global_vars.get('global_kg'))
    object_x = My_object('object_x')
    print()
 
    Pseudo_global_vars.set('global_kg', 1000)
    print('1: __main__ --> global vars list =',
          Pseudo_global_vars.names())
    print('1: __main__ --> is global_kg found? =',
          Pseudo_global_vars.contains('global_kg'))
    print('1: __main__ --> value of global_kg =',
          Pseudo_global_vars.get('global_kg'))
    object_x.print_global_vars(1)
    print()
 
    Pseudo_global_vars.drop('global_kg')
    print('2: __main__ --> global vars list =',
          Pseudo_global_vars.names())
    print('2: __main__ --> is global_kg found? =',
          Pseudo_global_vars.contains('global_kg'))
    print('2: __main__ --> value of global_kg =',
          Pseudo_global_vars.get('global_kg'))
    object_x.print_global_vars(2)
    print()
 
    sys.exit(0)


Файл my_object.py:
Кликните здесь для просмотра всего текста
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from global_vars import Pseudo_global_vars
 
class My_object(object):
    object_name = ''
 
    def __init__(self, name = 'My_object'):
        super(My_object, self).__init__()
        self.set_object_name(name)
        self.print_global_vars(0)
 
    def set_object_name(self, name):
        self.object_name = name
 
    def print_global_vars(self, i):
        print('{!s}: {!s} --> global vars list ='.format(i, self.object_name),
              Pseudo_global_vars.names())
        print('{!s}: {!s} --> is global_kg found? ='.format(i, self.object_name),
              Pseudo_global_vars.contains('global_kg'))
        print('{!s}: {!s} --> value of global_kg ='.format(i, self.object_name),
              Pseudo_global_vars.get('global_kg'))


Результаты выполнения:
Кликните здесь для просмотра всего текста
Код:
user@linux:~/MyProjects/qtprobe/global_probe> python3 main.py
0: __main__ --> global vars list = []
0: __main__ --> is global_kg found? = False
0: __main__ --> value of global_kg = None
0: object_x --> global vars list = []
0: object_x --> is global_kg found? = False
0: object_x --> value of global_kg = None

1: __main__ --> global vars list = ['global_kg']
1: __main__ --> is global_kg found? = True
1: __main__ --> value of global_kg = 1000
1: object_x --> global vars list = ['global_kg']
1: object_x --> is global_kg found? = True
1: object_x --> value of global_kg = 1000

2: __main__ --> global vars list = []
2: __main__ --> is global_kg found? = False
2: __main__ --> value of global_kg = None
2: object_x --> global vars list = []
2: object_x --> is global_kg found? = False
2: object_x --> value of global_kg = None
user@linux:~/MyProjects/qtprobe/global_probe>


И нельзя не упомянуть про ограничения.

Ведь послуживший примером для подражания класс QSqlDatabase порождает и хранит подобным образом только экземпляры своего класса. При этом сам класс QSqlDatabase не наследуется от класса QObject. Поэтому все порождаемые им соединения не вступают в родительские отношения с теми объектами, в которых были выполнены вызовы QSqlDatabase.addDatabase(), что позволяет этим соединениям спокойно существовать даже после удаления этих объектов.

Предложенное здесь решение это никак не контролирует. И скорее всего следует ожидать неприятностей, если где-то в произвольном месте программы удалить родителя дочернего объекта, сохранённого в такой "глобальной переменной". Потому как этот дочерний объект будет удалён вместе с родительским.

Впрочем и некий бонус появляется — в качестве псевдонима годится любая строка, в том числе и на родном — великом и могучем По крайней мере, некоторая часть комментариев становится не нужна.

PS: поправил форматирование текста. Однако, спойлеры имеют значение На широких-то экранах это не ощущается, а на маломерках просто срам

PPS: впрочем, помогло не очень Хотя в предпросмотре редактора записи смотрелось приемлемо.
Размещено в Памятка
Показов 2556 Комментарии 19
Всего комментариев 19
Комментарии
  1. Старый комментарий
    Аватар для Avazart
    К чему сыр-бор? Ну да в Qt есть такое и лично мое мнение - это плохо в плане архитектуре.
    Поэтому я всегда указываю конкретную дб с которой имею дело.
    Запись от Avazart размещена 14.11.2021 в 14:56 Avazart вне форума
  2. Старый комментарий
    iamvic к сожалению я не могу оценить Ваши наработки, так как не знаю и не работаю с PyQt. Но я поддерживаю Ваше стремление освободится от ограничений, там где они становятся тормозом.
    Шаблоны хороши тем что они упрощают мышление, но если жестко следовать им, то они ограничивают мышление. Все прорывные открытия происходили тогда, когда вырывались за пределы шаблонов.
    Удачи в Ваших изысканиях.
    Запись от Viktorrus размещена 15.11.2021 в 19:22 Viktorrus вне форума
  3. Старый комментарий
    Viktorrus, как это? Там же из Qt только идея съобезьянена, а в реализации голый Python...
    Запись от iamvic размещена 15.11.2021 в 19:40 iamvic вне форума
  4. Старый комментарий
    Аватар для Avazart
    Это просто дичь.
    Запись от Avazart размещена 15.11.2021 в 20:15 Avazart вне форума
  5. Старый комментарий
    iamvic может у Вас использован только питон, но Вы ссылаетесь на Qt, и Ваши ссылки мне не понятны, и это усложняет понимание Вашего кода. Извините, но мне сейчас не хочется разбираться в Вашем коде, так как голова занята другим проектом. Может быть потом, когда будет свободное время, попробую разобраться. Но только если для этого не понадобится понимание работы Qt.
    Запись от Viktorrus размещена 15.11.2021 в 20:43 Viktorrus вне форума
  6. Старый комментарий
    Viktorrus, нет проблем! Обращайтесь, если что...
    Запись от iamvic размещена 15.11.2021 в 20:57 iamvic вне форума
  7. Старый комментарий
    Avazart, давайте на этом и остановимся.
    Запись от iamvic размещена 15.11.2021 в 23:35 iamvic вне форума
  8. Старый комментарий
    Похоже страсти ненужные улеглись, можно и продолжить.

    Оставим в стороне обсуждение вопросов типа "хорошо архитектуре или плохо", поскольку там, по определению, главной направляющей и движущей силой является архитектурный стиль и выносить на публику эту глубоко интимную вещь смысла нет. Лучше зададимся вопросом: "А имеет ли право на жизнь послуживший примером для моей поделки способ получения соединения с базой данных, реализованный в QSqlDatabase?"

    На мой непросвещённый взгляд, это решение Qt/PyQt вполне годно и для применения в многопоточных приложениях. Там ведь требуется создавать уникальное соединение прямо в дополнительном потоке. А это очень просто делается именно этим способом.

    Скупиться на объяснения не буду - расскажу подробно. Надо всего лишь:

    1. создать свой подкласс, унаследовав его от QThread;
    2. реализовать в нём дополнительные методы, позволяющие через потоконезависимые переменные передавать в дополнительный поток параметры соединения: хост, порт, логин, пароль, имя БД и так далее;
    3. в перегруженном методе run() предусмотреть последовательное выполнение следующих действий:
    - сгенерировать уникальное имя соединения для использования в QSqlDatabase.addDatabase(),
    - получить переданные извне параметры соединения,
    - создать экземпляр соединения с помощью QSqlDatabase.addDatabase(), используя сгенерированное имя соединения,
    - задать параметры соединения в полученном экземпляре и открыть его.
    - выполнять свою работу, периодически отправляя сигналы, содержащие данные для обработки в основном потоке.
    - по завершению работы - закрыть соединение,
    - а затем удалить его с помощью QSqlDatabase.removeDatabase()

    А при создании дополнительного потока требуется только передать ему параметры соединения до его запуска методом start().

    Как мне представляется, в этом случае неплохим бонусом является также возможность контроля наличия соединений с помощью QSqlDatabase.connectionNames() из любой точки программы.
    Запись от iamvic размещена 18.11.2021 в 15:26 iamvic вне форума
  9. Старый комментарий
    iamvic для меня всегда критерием истины была практика. Если у Вас это реально работает, то поздравляю. Не зависимо от того, существуют ли другие методы передачи данных между потоками, то что Вы имеете свой метод, позволяет вносить в него изменения, позволяющие Вам его приспосабливать под конкретные задачи, что не всегда легко с чужими программами.
    Запись от Viktorrus размещена 18.11.2021 в 22:08 Viktorrus вне форума
  10. Старый комментарий
    Аватар для Avazart
    Цитата:
    На мой непросвещённый взгляд, это решение Qt/PyQt вполне годно и для применения в многопоточных приложениях.
    Стоит только помнить что питон вообще плохо применим для многопоточных приложений.
    Как еще шутят что в питоне нет "многопоточности" есть только "полупоточность".

    Цитата:
    Оставим в стороне обсуждение вопросов типа "хорошо архитектуре или плохо", поскольку там, по определению, главной направляющей и движущей силой является архитектурный стиль и выносить на публику эту глубоко интимную вещь смысла нет.
    С таким подходом лучше оставить программирование.
    Так как не стоит путать стиль и отсутствие такового.

    Цитата:
    1. создать свой подкласс, унаследовав его от QThread;
    Сразу плохое начало. Ванегую что далее будет неверное его использование.
    Запись от Avazart размещена 18.11.2021 в 22:43 Avazart вне форума
    Обновил(-а) Avazart 18.11.2021 в 22:48
  11. Старый комментарий
    Viktorrus, ну хватит уже этой демагогии. Ближе к делу. Вы же с кодом до сих пор не разобрались. На хрена мне эти Ваши благие рассуждения?
    Запись от iamvic размещена 18.11.2021 в 23:13 iamvic вне форума
  12. Старый комментарий
    Avazart,
    Цитата:
    Как еще шутят что в питоне нет "многопоточности" есть только "полупоточность".
    Дыкть другой-то всё-равно нет. Пользуем то, что есть.
    Цитата:
    Так как не стоит путать стиль и отсутствие такового.
    Этт точно! Нежелание спорить о вкусах свидетельствует об отсутствии вкуса. (надо занести в мемориз)
    Цитата:
    Сразу плохое начало
    Цитирую отсюда https://doc.qt.io/qt-5/threads-technologies.html :
    Цитата:
    Place the function in a reimplementation of QThread::run() and start the QThread. Emit signals to update progress.
    Ну обнародуйте другой способ, которым можно реимплементировать этот самый QThread.run() в питоне.
    Это ж питон, а не C++, где наверное уже без этого обходятся? Не так ли?
    Запись от iamvic размещена 18.11.2021 в 23:47 iamvic вне форума
  13. Старый комментарий
    Аватар для Avazart
    Цитата:
    Дыкть другой-то всё-равно нет. Пользуем то, что есть.
    Есть С++ и другие языки.
    Так же в некоторых случая допустимо использовать процессы.
    Цитата:
    Этт точно! Нежелание спорить о вкусах свидетельствует об отсутствии вкуса. (надо занести в мемориз)
    Стиль кода это не вкус это обязательна вещь для программиста.

    Цитата:
    Это ж питон, а не C++, где наверное уже без этого обходятся? Не так ли?
    В плане использования QThread все так же как в С++. Смотрите примеры на С++ из той же документации там увидите пример использования Worker класса.
    Не то что бы наследовать неправильно, это скорее менее очевидно и как правило ведет к ошибкам в коде.
    Что бы использовать наследование нужны какое-то обоснование.
    Запись от Avazart размещена 19.11.2021 в 14:13 Avazart вне форума
    Обновил(-а) Avazart 19.11.2021 в 14:16
  14. Старый комментарий
    Avazart,
    Цитата:
    Есть С++ и другие языки.
    Конечно, есть. Но к несчастью, этот блог посвящён питону и здесь будет только питон и про питон (вот такой каламбурчик получается).
    Цитата:
    Стиль кода это не вкус это обязательна вещь для программиста.
    Кто ж спорит? Только всё равно у каждого есть свои индивидуальные особенности при всей внешней общности. Поэтому даже в шагающей строем корпоративной команде программистов каждому что-то где-то жмёт и что-то где-то натирает. Потому что каждый из них лучше других знает чей стиль самый лучший . Но в команде они вынуждены подстраиваться под общий стиль. А переходя в другую команду, не споря подстраиваются под принятый там стиль.

    А спорить чей стиль лучше, по моему мнению, это пустопорожняя болтовня. Чтоб получился какой-то полезный выхлоп с этих споров, надо же на что-то опираться. А для этого надо иметь какое-никакое формализованное описание своего стиля. Вот тогда можно будет провести хоть какое-то сравнение и сделать более-менее обоснованные выводы.

    Но как-то не наблюдается в округе войн стилей, где кипели бы страсти и спорящие швыряли бы друг в друга UML-ки, поражающие своей крутизной. Все тихо. Отдельные выкрики "Плохой стиль! Плохо для архитектуры!" обычно пропускаются мимо ушей, поскольку не сопровождаются формализованным обоснованием.
    Цитата:
    Смотрите примеры на С++ из той же документации там увидите пример использования Worker класса.
    Вы меня удивляете! Здесь мне требовался как раз одноразовый QThread, чтобы "выстрелил и забыл". А отработавши, доложился и удалился.

    А вот "использование Worker класса" - это песня из другой оперы. Это QThread с временем жизни от пуска программы до её завершения (permanent), это специально обученный QObject и moveToThread(). Здесь мне это не требовалось.

    И, кроме того, Вы прекрасно знаете, что у меня в блоге есть пара записей на эту тему, где я как раз и упражнялся с упомянутым Вами вариантом использования QThread, сочиняя свой "тренажёрчик" в попытке понять что можно делать, а что нельзя, какие там особенности, неожиданности и прочее (здесь Иллюстрация к вопросу о многопоточности в PyQt5. и здесь Обновление к вопросу многопоточности в PyQt5). Вы же там в комментариях отмечались. Да, там не идеальный код, жуткий стиль, куча других недостатков, которые надо бы устранить, что однако не мешает выполнению основной задачи и позволяет в какой-то степени понять происходящие процессы.
    Запись от iamvic размещена 19.11.2021 в 21:56 iamvic вне форума
  15. Старый комментарий
    "Viktorrus, ну хватит уже этой демагогии. Ближе к делу. Вы же с кодом до сих пор не разобрались. На хрена мне эти Ваши благие рассуждения?"
    Грубо. Я занимаюсь разработкой своей технологии и в Вашей технологии у меня на данный момент нет реальной необходимости. Но теперь, после Вашей грубости, боюсь у меня уже и не появится желание вникать в Ваш проект, что бы дать свою оценку.
    Желаю удачи.
    Запись от Viktorrus размещена 19.11.2021 в 22:18 Viktorrus вне форума
  16. Старый комментарий
    Аватар для Avazart
    Цитата:
    Конечно, есть. Но к несчастью, этот блог посвящён питону и здесь будет только питон и про питон (вот такой каламбурчик получается).
    Цитата:
    Да, там не идеальный код, жуткий стиль, куча других недостатков, которые надо бы устранить, что однако не мешает выполнению основной задачи
    Скорее быдлоКот про быдлоКод. Как такой кАЛОмбурчик?

    Цитата:
    Вы меня удивляете! Здесь мне требовался как раз одноразовый QThread, чтобы "выстрелил и забыл". А отработавши, доложился и удалился.

    А вот "использование Worker класса" - это песня из другой оперы. Это QThread с временем жизни от пуска программы до её завершения (permanent), это специально обученный QObject и moveToThread(). Здесь мне это не требовалось.
    Глупость. Сначала что ли посмотрите примеры из документации а если нет почитайте статьи на эту тему.

    P.S: И освойте что ли git наконец.
    Запись от Avazart размещена 20.11.2021 в 00:12 Avazart вне форума
    Обновил(-а) Avazart 20.11.2021 в 00:16
  17. Старый комментарий
    Avazart,
    Цитата:
    Как такой кАЛОмбурчик?
    Эко Вас порвало-то Сокровенное попёрло? Но Вы ж вроде бы не муха, чтоб "почуяв восхительный запах, немедленно полететь подкрепиться"?
    Цитата:
    Глупость. Сначала что ли посмотрите примеры из документации а если нет почитайте статьи на эту тему.
    Ну, блин, соблюдайте же элементарные правила вежливости в конце концов - обосновывайте своё мнение. И если уж посылаете, то посылайте адресно, указав ссылочки и своё толкование изложенного там, как этого требуют правила форума.

    А если есть претензии к моему коду, то он лежит тут в открытом доступе. Не составляет труда ткнуть пальцем в проблемные места и доходчиво объяснить почему, даже матерно. И я Вас пойму. Все мы учимся чему-нибудь всю жизнь...

    Начните хотя бы с этого (цитирую отсюда Многопоточные технологии в Qt раздел примеры использования):
    Цитата:
    1. время жизни потока — постоянно,
    2. функционирование — с помощью объекта, живущего в дополнительном потоке, который может выполнять различные задачи по запросу и/или получать новые данные для работы,
    3. реализация — создать подкласс QObject для создания исполнителя работ, создать экземпляр исполнителя (объект Worker) на базе этого подкласса и экземпляр дополнительного потока QThread, переместить экземпляр исполнителя во вновь созданный поток, отправлять команды или данные объекту исполнителя через соединения сигнал-слот с постановкой в очередь.
    Что не так в моём толковании использования объекта Worker? Что не так в реализации этого решения в моём "тренажёрчике"? Чем она противоречит примеру, приведённому здесь QThread Class после слов
    Цитата:
    You can use worker objects by moving them to the thread using QObject::moveToThread().
    Изложите Вашу позицию развёрнуто и будет Вам счастье - Вас поймут.

    Все эти мои претензии к Вашей манере вести беседу, как видите, весьма обоснованы.

    Если же Вы не тянете взятую на себя роль цензора, то сдерживайте себя, сожмите в кулачок весь свой пыл к выставлению оценочек и помалкивайте. Обосновать их Вы всё равно не сможете, а Ваше личное необоснованное мнение никого не интересует - авторитетов здесь нет.

    Напоминаю ещё раз, что в этом блоге Вы в гостях. Здесь можно послать куда угодно и высказать любое мнение в любой форме. Но это должно быть обосновано. Повторюсь, авторитетов нет, помощь приму с благодарностью, на слово не верю и быковать тут не стоит.
    Запись от iamvic размещена 20.11.2021 в 13:01 iamvic вне форума
  18. Старый комментарий
    Аватар для Avazart
    Цитата:
    Ну, блин, соблюдайте же элементарные правила вежливости в конце концов - обосновывайте своё мнение. И если уж посылаете, то посылайте адресно, указав ссылочки и своё толкование изложенного там, как этого требуют правила форума.
    Ссылки на доку что ли? Ну ок
    https://doc.qt.io/qt-5/qthread.html#details
    Первый пример.

    Касательно статей лень гуглить и перебирать что бы сейчас найти что-то действительно толковое.
    Но если сходу и не придираясь:
    https://habr.com/ru/post/150274/

    Т.е. что за манера все попрошайничать и совершенно не пытаться нагуглить самому?

    Цитата:
    А если есть претензии к моему коду, то он лежит тут в открытом доступе.
    Где? Пока Вы не выложите на гитхаб мне банально лень смотреть и тем более тыкать.
    Запись от Avazart размещена 21.11.2021 в 00:18 Avazart вне форума
    Обновил(-а) Avazart 21.11.2021 в 00:26
  19. Старый комментарий
    Ну, всё, Avazart, доигрались!

    Чтобы воспринимать моё скромное законное требование обосновать Вашу личную позицию, подтвердив её ссылками и развёрнутым описанием Вашего личного понимания того, о чём там говорится (а без Вашего подробного объяснения Ваша личная позиция так и останется нераскрытой), как попытку выклянчить у Вас что-то - это ж надо обладать таким извращённым мышлением, что я просто теряюсь.

    Считайте, что Вы своего добились - права голоса Вы отныне лишены.
    Запись от iamvic размещена 21.11.2021 в 10:21 iamvic вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru