Форум программистов, компьютерный форум, киберфорум
Python для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.88/8: Рейтинг темы: голосов - 8, средняя оценка - 4.88
 Аватар для GulgDev
132 / 118 / 29
Регистрация: 09.07.2019
Сообщений: 1,071

self не имеет атрибута

08.06.2020, 10:52. Показов 1954. Ответов 33
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Я использую multithreading для того, чтобы вызывать функции не в главном потоке, а в другом. Но почему то в функции destroy self не имеет атрибута __hWnd__, хотя я его не удалял:
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import multithreading
 
import datetime
 
import json
 
from collections import namedtuple
Event = namedtuple('Event', 'window type time keyname keystate')
Event.__new__.__defaults__ = (None, None)
 
import __main__, os
file = __main__.__file__
 
import ctypes
import ctypes.wintypes as wintypes
 
kernel32 = ctypes.windll.kernel32
user32 = ctypes.windll.user32
gdi32 = ctypes.windll.gdi32
 
CW_USEDEFAULT = -2147483648
IDI_APPLICATION = 32512
WS_OVERLAPPEDWINDOW = 13565952
 
CS_HREDRAW = 2
CS_VREDRAW = 1
 
IDC_ARROW = 32512
WHITE_BRUSH = 0
 
SW_SHOWNORMAL = 1
 
counter = -1;
 
WNDPROC = ctypes.WINFUNCTYPE(
    ctypes.c_long,
    ctypes.c_int,
    ctypes.c_uint,
    ctypes.c_int,
    ctypes.c_int,
)
 
class WNDCLASS(ctypes.Structure):
    _fields_ = [('style',         ctypes.c_uint),
                ('lpfnWndProc',   WNDPROC),
                ('cbClsExtra',    ctypes.c_int),
                ('cbWndExtra',    ctypes.c_int),
                ('hInstance',     ctypes.c_int),
                ('hIcon',         ctypes.c_int),
                ('hCursor',       ctypes.c_int),
                ('hbrBackground', ctypes.c_int),
                ('lpszMenuName',  ctypes.c_wchar_p),
                ('lpszClassName', ctypes.c_wchar_p)]
 
class POINT(ctypes.Structure):
    _fields_ = [('x', ctypes.c_long),
                ('y', ctypes.c_long)]
 
class MSG(ctypes.Structure):
    _fields_ = [('hwnd',    ctypes.c_int),
                ('message', ctypes.c_uint),
                ('wParam',  ctypes.c_int),
                ('lParam',  ctypes.c_int),
                ('time',    ctypes.c_int),
                ('pt',      POINT)]
 
class __WindowMethods__():
    def show(self):
        def show():
            user32.ShowWindow(ctypes.c_int(self.__hWnd__), ctypes.c_int(8))
            user32.UpdateWindow(ctypes.c_int(self.__hWnd__))
        self.__thread__.call(show);
    def hide(self):
        def hide():
            user32.ShowWindow(ctypes.c_int(self.__hWnd__), ctypes.c_int(0))
        self.__thread__.call(hide);
class Window():
    with open(os.path.split(__file__)[0] + '\\events.json') as events_json:
        __events__ = json.loads(events_json.read());
    __pressed__ = [];
    def __procedure__(self, hWnd, message, wParam, lParam):
        if message == 2:
            self.__thread__.end();
            user32.PostQuitMessage(0);
            for name in set(dir(self)) - set(dir(type('type', (), {}))):
                setattr(self, name, None);
                delattr(self, name);
            return 0;
        elif message == 256:
            keyname = Window.__events__[str(wParam)];
            if keyname not in self.__pressed__:
                self.__pressed__.append(keyname);
                for function in self.listener['ANY']:
                    function.__globals__['event'] = Event(self, 'keypress', datetime.datetime.now().time(), keyname, 'DOWN');
                    function();
                for function in self.listener[keyname]:
                    function.__globals__['event'] = Event(self, 'keypress', datetime.datetime.now().time(), keyname, 'DOWN');
                    function();
            return 0;
        elif message == 257:
            keyname = Window.__events__[str(wParam)];
            self.__pressed__.remove(keyname);
            for function in self.listener['ANY']:
                function.__globals__['event'] = Event(self, 'keypress', datetime.datetime.now().time(), keyname, 'UP');
                function();
            for function in self.listener[keyname]:
                function.__globals__['event'] = Event(self, 'keypress', datetime.datetime.now().time(), keyname, 'UP');
                function();
            return 0;
        return user32.DefWindowProcW(ctypes.c_int(hWnd), ctypes.c_int(message), ctypes.c_int(wParam), ctypes.c_int(lParam));
    
    __wndclass__ = WNDCLASS()
    __wndclass__.style          = CS_HREDRAW | CS_VREDRAW
    __wndclass__.cbClsExtra     = 0
    __wndclass__.cbWndExtra     = 0
    __wndclass__.hInstance      = kernel32.GetModuleHandleW(ctypes.c_int(0))
    __wndclass__.hIcon          = user32.LoadIconW(ctypes.c_int(0), ctypes.c_int(IDI_APPLICATION))
    __wndclass__.hCursor        = user32.LoadCursorW(ctypes.c_int(0), ctypes.c_int(IDC_ARROW))
    __wndclass__.hbrBackground  = gdi32.GetStockObject(ctypes.c_int(0))
    __wndclass__.lpszMenuName   = None
    
    def __init__(self, title):
        global counter
        counter += 1
        self.__wndclassname__ = "PyWin_" + os.path.splitext(file)[0] + "#" + str(counter);
        def CreateWin():
            self.__wndclass__.lpfnWndProc    = WNDPROC(self.__procedure__);
            self.__wndclass__.lpszClassName  = self.__wndclassname__;
            
            if not user32.RegisterClassW(ctypes.byref(self.__wndclass__)):
                raise WinError()
            
            CreateWindowEx          = user32.CreateWindowExW
            CreateWindowEx.argtypes = [
                ctypes.c_int,
                ctypes.c_wchar_p,
                ctypes.c_wchar_p,
                ctypes.c_int,
                ctypes.c_int,
                ctypes.c_int,
                ctypes.c_int,
                ctypes.c_int,
                ctypes.c_int,
                ctypes.c_int,
                ctypes.c_int,
                ctypes.c_int,
            ]
            self.__hWnd__ = CreateWindowEx(
                0,
                self.__wndclass__.lpszClassName,
                title,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                0,
                0,
                self.__wndclass__.hInstance,
                0,
            )
        self.__thread__ = multithreading.Thread(self.__wndclassname__);
        self.__thread__.start();
        self.__thread__.call(CreateWin);
        self.listener = Listener();
    def __str__(self):
        return self.__wndclassname__;
    def __repr__(self):
        return str(self.__hWnd__);
    def start(self):
        def start():
            user32.ShowWindow(ctypes.c_int(self.__hWnd__), ctypes.c_int(1))
            user32.UpdateWindow(ctypes.c_int(self.__hWnd__))
            msg = MSG()
            pMsg = ctypes.pointer(msg)
            while user32.GetMessageW(pMsg, ctypes.c_int(0), 0, 0) != 0:
                user32.TranslateMessage(pMsg)
                user32.DispatchMessageW(pMsg)
            self.start = None; del self.start;
            for name in set(dir(__WindowMethods__)) - set(dir(type('type', (), {}))):
                setattr(self, name, getattr(__WindowMethods__, name));
        self.__thread__.call(start);
    def destroy(self):
        def destroy():
            user32.DestroyWindow(ctypes.c_int(self.__hWnd__));
        self.__thread__.call(destroy);
class Listener():
    bindings = {}
    with open(os.path.split(__file__)[0] + '\\events.json') as events_json:
        __events__ = json.loads(events_json.read());
    def onpress(self, key, function):
        if key in list(Listener.__events__.values()) + ['ANY', 'ARROW', 'NUMBER', 'LETTER']:
            if key not in self.bindings:
                self.bindings[key] = [function];
            else:
                self.bindings[key].append(function);
        else:
            raise ValueError("bad keyname '" + str(key) + "'");
    def onkeydown(self, key, function):
        def func():
            if event.keystate == 'DOWN':
                function.__globals__['event'] = event;
                function();
        self.onpress(key, func);
    def onkeyup(self, key, function):
        def func():
            if event.keystate == 'UP':
                function.__globals__['event'] = event;
                function();
        self.onpress(key, func);
    def __getitem__(self, name):
        return tuple(self.bindings.get(name, ()));
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
08.06.2020, 10:52
Ответы с готовыми решениями:

Объект не имеет атрибута, хотя он его имеет. Почему так?
Друзья! Ковыряюсь в классах. Пытаюсь понять логику поведения как она задумана создателями, ибо в книгах ничего про это не пишут. ...

Модуль 'sklearn' не имеет атрибута 'cross_validation'
Пытаюсь разбить свой набор данных, но я получаю эту ошибку: ImportError: cannot import name 'cross_validation' from 'sklearn'...

QuerySet не имеет атрибута name
При загрузки страницы вылетает ошибка 'QuerySet' object has no attribute 'name'. Что мне исправить? это models.py class...

33
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
08.06.2020, 16:28
Зачем делать вложенные функции внутри методов?
Больше всякой ерунды - больше боли.
0
 Аватар для GulgDev
132 / 118 / 29
Регистрация: 09.07.2019
Сообщений: 1,071
08.06.2020, 16:34  [ТС]
Цитата Сообщение от Garry Galler Посмотреть сообщение
Зачем делать вложенные функции внутри методов?
Garry Galler,
Цитата Сообщение от Hyppoprogramm Посмотреть сообщение
Я использую multithreading для того, чтобы вызывать функции не в главном потоке, а в другом.
0
1732 / 970 / 199
Регистрация: 22.02.2018
Сообщений: 2,693
Записей в блоге: 6
08.06.2020, 16:52
Hyppoprogramm, Не вникая в Ваш код, так как я не изучал много поточные режимы, хочу напомнить, что self это технический указатель питона, используемый только внутри определения класса. При работе программы, такого объекта не существует, вместо self, когда идет обращение к классу-объекту, автоматически подставляется объект-экземпляр класса. Я не знаю внутренностей питона, но не исключено, что self это просто ячейка в классе-объекте (размещаемом в оперативной памяти) куда вставляется передаваемый экземпляр класса, перед тем, как начинает выполняться код в теле класса.
Говорим self, подразумеваем экземпляр класса.
Говорим атрибут у self, подразумеваем атрибут у экземпляра класса.
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
08.06.2020, 17:02
Цитата Сообщение от Hyppoprogramm Посмотреть сообщение
Я использую multithreading для того, чтобы вызывать функции не в главном потоке, а в другом.
Какое отношение это имеет к моему вопросу?

Python
1
2
3
4
5
6
7
8
9
10
class Test:
    def __init__(self):
        
        def create():
            self.__id__ = 100
            
            
            
obj = Test()
print(obj.__id__)
Code
1
AttributeError: 'Test' object has no attribute '__id__'
1
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
08.06.2020, 17:09
Hyppoprogramm,
Code
1
ModuleNotFoundError: No module named 'multithreading'
Code
1
FileNotFoundError: [Errno 2] No such file or directory: '\\events.json'
JSON для примера можно?
0
 Аватар для GulgDev
132 / 118 / 29
Регистрация: 09.07.2019
Сообщений: 1,071
08.06.2020, 17:18  [ТС]
Цитата Сообщение от outoftime Посмотреть сообщение
ModuleNotFoundError: No module named 'multithreading'
Его надо pip-ом установить.

Добавлено через 1 минуту
Garry Galler, Вам ни о чём не говорит слово call?
0
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
08.06.2020, 17:20
Hyppoprogramm, количество ошибок на квадратную строку в вашем коде зашкаливает. Надоело разгребвать, удачи!
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
08.06.2020, 17:21
Цитата Сообщение от Hyppoprogramm Посмотреть сообщение
Вам ни о чём не говорит слово call
В ваше коде - нет.

Неужели вам непонятно, что у вас этот атрибут никак не устанавливается?
0
 Аватар для GulgDev
132 / 118 / 29
Регистрация: 09.07.2019
Сообщений: 1,071
08.06.2020, 17:22  [ТС]
Garry Galler, устанавливается.
0
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
08.06.2020, 17:26
Чем вам так многопоточность не угодила!?
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
08.06.2020, 17:29
Цитата Сообщение от Hyppoprogramm Посмотреть сообщение
устанавливается.
Как? Просто это не я с вами не согласен. Это Python не согласен. Будете с ним спорить?
0
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
08.06.2020, 17:38
Цитата Сообщение от Garry Galler Посмотреть сообщение
Как?
Python
1
2
3
4
5
6
7
    def __init__(self, title):
        def CreateWin():
            self.__hWnd__ = CreateWindowEx()
        #…
        self.__thread__ = multithreading.Thread(self.__wndclassname__);
        self.__thread__.start();
        self.__thread__.call(CreateWin);
Правда, я не уверен, что функция CreateWin() вызывалась хоть раз и именно в том порядке, как хочет ТС.
0
 Аватар для GulgDev
132 / 118 / 29
Регистрация: 09.07.2019
Сообщений: 1,071
08.06.2020, 17:40  [ТС]
Рыжий Лис, как раз многопоточность я и использую.
0
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
08.06.2020, 17:45
1) многопоточность используется не так
2) не вижу примитивы синхронизации
3) зачем вообще создавать поток ради запуска одной функции? Накладные расходы на создание потока выше.

Добавлено через 1 минуту
Воркеры: https://docs.python.org/3/libr... Queue.join
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
08.06.2020, 18:10
Цитата Сообщение от Рыжий Лис Посмотреть сообщение
Правда, я не уверен, что функция CreateWin() вызывалась хоть раз и именно в том порядке, как хочет ТС
Это очень странный подход использовать вложенные функции несвязанные (ubounded) с классом ни экземплярно, ни статически внутри класса. Это сильно снижает читабельность кода и может дать неконтролируемые побочные эффекты, если мы как-то случайно захватим переменную из внешней области видимости.
Одно дело, если бы это логически задуманное специальное замыкание и другое, когда, вот так, без всякого смысла.

Кстати, ведь экземпляр такой функции создается единожды при определении класса. И значение self должно также захватываться единожды. Отсюда и отсутствие атрибута. Как мне кажется.
То есть self для CreateWin один, а для destroy - совсем другой. Ну или все дело, действительно, в порядке вызовов, когда атрибут еще не успевает присвоиться экземпляру.
1
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
08.06.2020, 18:34
Рыжий Лис, Garry Galler, а я на https://pypi.org/project/MultiThreading/ грешу, вот скажите мне, зачем они в примере добавили
Python
1
thread.end() # releasing the thread so that the program can exit
Добавлено через 1 минуту
Ха, вот че:
Author: Hyppoprogramm
Мейнтейнер наш (:

Добавлено через 1 минуту
И хрена с 2 я эту либу буду ставить без исходников, мало ли чего он в setup.py напихал
1
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
08.06.2020, 18:43
outoftime, https://www.cyberforum.ru/blog... g6622.html
1
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
08.06.2020, 19:23
Рыжий Лис, ага, ну логика стала понятней, но без кода всё равно стрёмно + если он здесь ошибок наделал, что же он там сделал? В любом случае, я пас.
0
08.06.2020, 19:38  [ТС]

Не по теме:

outoftime, хотите код?
Вот, можете посмотреть:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import threading, functools, sys
 
class MultiThreadingError(Exception):
    __module__ = 'builtins'
    def __init__(self, name, message = None, exception = None):
        if exception == None:
            self.args = (name, None, message);
        else:
            self.args = (name, exception.__class__.__name__, str(exception) if message == None else message);
    def __str__(self):
        return self.args[0] + ': ' + (self.args[1] + ': ' if self.args[1] != None else '') + self.args[2];
class Thread():
    name = property(lambda self: self.__name)
    threads = [];
    def __init__(self, name):
        self.threads = None;
        del self.threads;
        name = str(name);
        for thread in Thread.threads:
            if thread.name == name:
                raise ValueError;
        Thread.threads.append(self);
        self.__name    = name;
        self.__queue   = [];
        self.__work    = False;
        self.__wait    = True;
        def thread(self):
            while self.__work:
                while self.__pause:
                    pass;
                if len(self.__queue) > 0:
                    self.__working = True;
                    e = False;
                    try:
                        self.__queue[0]();
                    except Exception as exception:
                        e = exception;
                    if e != False:
                        raise MultiThreadingError(self.__name, e);
                    self.__queue = self.__queue[1:];
                else:
                    if not self.__wait:
                        break;
        self.__thread = threading.Thread(target=thread, name=name, args=(self,));
    def __str__(self):
        return self.__name;
    def __repr__(self):
        return str(self);
    def start(self):
        self.__work = True;
        self.__thread.start();
    def end(self):
        self.__wait = False;
    def stop(self):
        self.__work = False;
        self.__thread.join();
        def hook(*args):
            raise MultiThreadingError(self.__name, 'cannot use thread (thread was stopped)');
        self.__getattribute__ = self.__setattribute__ = self.__delattribute__ = hook;
    def call(self, function, *args, **kwargs):
        function.__globals__['thread'] = self;
        function = functools.partial(function, *args, **kwargs);
        self.__queue.append(function);
    def prepend(self, function, *args, **kwargs):
        function.__globals__['thread'] = self;
        function = functools.partial(function, *args, **kwargs);
        self.__queue.prepend(function);
    def takeoff(self, function):
        try:
            self.__queue.remove(function);
            self.__values[function] = None;
            del self.__values[function];
        except:
            return False;
        return True;
 
def call(function, *args, **kwargs):
    thread = Thread(function.__name__ + '#' + str(id(function)));
    thread.call(function, *args, **kwargs);
    thread.end();
 
def find(name):
    for thread in Thread.threads:
        if thread.name == name:
            return thread;

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
08.06.2020, 19:38
Помогаю со студенческими работами здесь

Объект 'NoneType' не имеет атрибута 'name'
Добрый день, уважаемые знатоки, решил для себя узнать Питон и Джанго, но столкнулся с данной проблемой: при поисковом запросе открывается...

Как в grab ввести какой-либо текст в форму, которая не имеет атрибута input?
Нужно на грабе сделать заполнение формы на одном сайте, но она не имеет атрибута input (там встроенный текстовый редактор, как на этом...

Какое значение имеет больший приоритет: значение свойства, указанное в стиле, или значение атрибута элемента
Уважаемые, можете объяснить и по возможности выполнить одну задачу , буду благодарен Проверьте, какое значение имеет больший...

Селектор атрибута с префиксом пространства имён в имени атрибута
Здравствуйте. Нужна помощь. В сети так и не нашла ответ на свой вопрос. Есть элемент: <div abc:element-id="29"...

Как можно присвоить значение атрибута одного XML файла атрибута из другого XML файла?
привет, при генерации с помощью SAX некоторого xml файла, как можно присвоить точно такое же значение аттрибуту этого файла аттрибута из...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Первый деплой
lagorue 16.01.2026
Не спеша развернул своё 1ое приложение в kubernetes. А дальше мне интересно создать 1фронтэнд приложения и 2 бэкэнд приложения развернуть 2 деплоя в кубере получится 2 сервиса и что-бы они. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь постоянного тока с R, L, C, k(ключ), U, E, J. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа, решает её и находит токи на L и напряжения на C в установ. режимах до и. . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Изучаю kubernetes
lagorue 13.01.2026
А пригодятся-ли мне знания kubernetes в России?
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru