середа, 20 грудня 2017 р.

Batch script for removing extra symbols after extension

I try to develope my English skills that is why I going to write articles on English. Please enjoy :) 

Script which I have been using for deleting extra right's symbols after extension or other lookup string:

@echo off
setlocal EnableDelayedExpansion

set /p pth=Please input the path to the folder:
set /p ext=Please input the extension:
set replace=

for %%a in ("%pth%\*") do (
set fpath=%%~dpa

set fileVariable=%%~nxa
REM echo.!fileVariable!

CALL SET delPart=%%fileVariable:*%ext%=%%
REM echo.!delPart!

IF defined delPart (
CALL set string=%%fileVariable:!delPart!=!replace!%%
REM echo.!string!

IF defined string (
set "fullNameB=!fpath!!fileVariable!"
REM echo !fullNameB!

ren "!fullNameB!" "!string!"
echo "File: !fileVariable! was renamed to !string!"
)
)

)


pause

понеділок, 10 липня 2017 р.

Список COM-портов в Delphi

Появилась задачка определять к какому COM-порту подключен кассовый аппарат. Стал искать как получить список доступных портов в системе. В итоге нагуглил вот такой способ через реестр:
uses Registry;
procedure getCOM_ports();
var
reg: TRegistry;
st: Tstrings;
begin
reg := TRegistry.Create(KEY_READ);
try
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey('hardware\devicemap\serialcomm', False);
st := TstringList.Create;
try
reg.GetValueNames(st);
for i := 0 to st.Count - 1 do
begin
ShowMessage(reg.Readstring(st.strings[i]));
end;
finally
st.Free;
end;
reg.CloseKey;
finally
reg.Free;
end;
end;
view raw COM-ports hosted with ❤ by GitHub

середа, 5 липня 2017 р.

Code Assistant в PL/SQL Developer не работает

Восстановившись после petya.a я столкнулся с некоторыми проблемами, одной из которых была неработоспособность функции подсказки названия объектов БД в PL/SQL Developer. Функционал этот называется Code Assistant и включается в настройках:
Но что бы я ни делал, при вводе 3 первых символов названия таблицы/процедуры/пакета/т.д. подсказка не выдавалась. Также ничего не происходило  и при нажатии на F6 (клавиша по-умолчанию для вызова Code Assistant). Точнее какие-то подсказки выдавались, например, если написать SEL, то появится подсказка SELECT и т.д., но объекты моей схемы я не видел.
Поиски по интернету привели меня к тому, что Code Assistant берет данные с запроса, который находится в файле CANames.sql в папке PLSQL Developer. Открыв файл я увидел иероглифы и все сразу стало на свои места: вирус petya.a шифрует файлы *.sql. В итоге, я взял файл с компьютера, где все работало , перезапустил PL/SQL Developer и Code Assistant заработал.

Вот что должно находится в файле:
/*
The name + type results of these queries will be used by the Code Assistant
if the "Describe Context" option is enabled. After typing 3 or more characters
the Code Assistant will show a list of matching names.
Separate multiple queries with semi-colons and use the :schema bind variable
to restrict names to the currently connected user.
In case of an error the query results will be omitted. No error message will
be displayed.
Place this file in the PL/SQL Developer installation directory for all users,
or in the "%APPDATA%\PLSQL Developer" directory for a specific user.
*/
select object_name, object_type
from sys.all_objects o
where o.owner = :schema
and o.object_type in ('TABLE', 'VIEW', 'PACKAGE','TYPE', 'PROCEDURE', 'FUNCTION', 'SEQUENCE')
;
select s.synonym_name as object_name, o.object_type
from all_synonyms s, sys.all_objects o
where s.owner in ('PUBLIC', :schema)
and o.owner = s.table_owner
and o.object_name = s.table_name
and o.object_type in ('TABLE', 'VIEW', 'PACKAGE','TYPE', 'PROCEDURE', 'FUNCTION', 'SEQUENCE')
;
select db_link as object_name, 'DATABASE LINK' as object_type
from sys.all_db_links o
where o.owner = :schema
or o.owner = 'PUBLIC'
;
view raw CANames hosted with ❤ by GitHub

середа, 28 червня 2017 р.

petya.a и как я восстанавливался

Windows Defender на работе выдает сообщение, что найден вирус, жму удалить - пишет, что невозможно. Забиваю, сижу, пишу код и тут PL/SQL Developer говорит, что мой файл с запросами был изменен. Нажимаю показать изменения и вижу слева SQL, а справа "привет с Алиэкспресса" (иероглифы). К нам заходят програмеры с соседнего отдела и говорят про вирус, советуют выключать комп. Жму кнопку выключения, компьютер тухнет. Через час, в новостях уже во всю пишут про вирус petya.a.
Вечером захожу в ближайший магазин, покупаю флешку и иду домой делать ее загрузочной. Прогуглив, нахожу утилиту YUMI с помощью которой записываю на флешку 2 образа: Boot-Repair-Disk и Avira Rescue System
Винда 10-ка, загружать нельзя, так как сразу начинается шифрование файлов под видом чекдиска, а потом и предложение заплатить 300 долл. Я загрузил Авиру и прогнал полную проверку - нашло каких-то 10 объектов (кряки тоже вошли в список) и удалило. Далее я перезагрузился и запустил Boot-Repair, нажал на кнопку, как было описано в инструкции.
Снова перезагрузился и попробовал запустить десятку - появилась ошибка:
A disk read error occurred Press Ctrl+Alt+Del to restart 
Полез искать в интернет - найденные варианты не подошли; еще пару раз повторил вышеописанное - та же ошибка. Пробую методом научного тыка: в live-usb от авиры есть программка GParted, в ней можно посмотреть на разделы винчестера. У меня 2 винта: SSD, на котором установлена 10-ка и HDD, на котором раньше была 8-ка. Я замечаю, что флаг boot для 10 установлен на раздел, который в 8-ке такого флага не имеет. В Windows 8 флаг boot установлен для секции system reserved. Решаю, что для Windows 10 тоже надо поставить boot на system reserved. Перезагружаюсь. Начинается загрузка винды :)
Пару замечаний:
- Я также воспользовался рекомендацией по поводу файла perfc (ищите в интернете).
- После загрузки Windows некоторые программы не запускались, либо слетела лицензия.

BSOD 0x0000007B и Windows7

Запараноился я значит по поводу petya.a и решил последовать инструкции, а конкретнее этой части:
To enable or disable SMBv1 on the SMB server, configure the following registry key:
Registry subkey: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\ParametersRegistry entry: SMB1
REG_DWORD: 0 = Disabled
REG_DWORD: 1 = Enabled
Default: 1 = Enabled
 
To disable SMBv1 on the SMB client, run the following commands:
sc.exe config lanmanworkstation depend= bowser/mrxsmb20/nsi
sc.exe config mrxsmb10 start= disabled
 
А после этого накатил обновление майкософта. Перезагрузился и только начал смотреть как 4 цвета логотипа сливаются в единый флаг, всю эту идиллию, на секунду прервал синий экран. Увидев синий цвет снова я понял, что вечер перестает быть томным и попробовал разные методы загрузки: от безопасного до последней удачной. В итоге загрузившись с параметром "не перезагружаться в случае ошибки", я увидел полный текст BSOD: 
0x0000007B (0xFFFFF880009A97E8, 0xFFFFFFFFC0000034, 0x0000000000000000, 0x0000000000000000)
Попробовал вернутся к точке восстановления, которая была автоматически создана при установке пакета обновлений безопасности от Майкрософт - та же ошибка.
Привет, Гугл. Следующие пару часов я просидел на куче форумов и блогов, перепробовал кучу разных способов: 
- В режиме восстановления подкорректировать ключи реестра: установить значение в 0 для ключа Start ветки HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\msahci и ему подобных.
- Провести chkdsk /f/r 
- Выполнить BOOTREC с ключами FIXBOOT и FIXMBR
- sfc /scannow /OFFBOOTDIR=E:\ /OFFWINDIR=E:\windows (диск Е: - это, в моем случае, диск С: в режиме восстановления)
- и т.д. и в разных комбинациях.
В общем спать уже хочется, а конца истории не видно. И тут я попадаю на ЖЖ какого-то парня, у которого такая же ошибка, только винда 2008 и диск в рейде. Он тоже описывает, что гугл уже не тот и ничего ему не помогало до тех пор, пока он не скопировал файлы с папки system32/config/regback в папку system32/config (прежде чем все затирать надо сделать резервную копию). 
С помощью командной строки и команд copy, и xcopy я заменил файлы в папке system32/config. Ребут, скрещиваю пальцы и наблюдаю лого windows. На часах было 3 ночи :(
Еще несколько слов:
- В папке regback  хранится резервная копия реестра, которую система создает каждые десять дней, если планировщик заданий не отключен.
- В процессе восстановления работоспособности ОС вспоминаешь забытое и узнаешь новое (не воспринимайте как призыв к действию "поломай виндовз и проведи весело вечер").
- В следующий раз я буду делать точку восстановления перед внесением изменений в реестр (не повторяйте моих ошибок).

 

понеділок, 19 червня 2017 р.

ENABLE NOVALIDATE проверяет имеющиеся данные

Понадобился мне констрейнт для проверки дублирующих записей в таблице, в которой уже присутствовали дубли. Добавил NOVALIDATE при создании и получил ошибку:
ORA-02299: cannot validate - duplicate keys found

Как выяснилось, при создании уникального ограничения создается уникальный индекс (не всегда, если оракл находит подходящий индекс - он будет использован). Выходом из ситуации стало добавление ключевого слова DEFERRABLE:
alter table TBL_1
  add constraint UK_TBL_1#NO_DOUBLE unique (col1, col2, col3, col4, col5)
  deferrable
  novalidate

DEFERRABLE делает так, что создается НЕуникальный индекс и констрейнт благополучно создается.

Также, можно было создать неуникальный индекс с самого начала и уже потом создать NOVALIDATE констрейнт.

CDATA в XML

Для того, что бы в XML появился тег
<![CDATA[test]]>

надо использовать метод XMLCdata:
select XMLCdata('test')
from dual;

четвер, 25 травня 2017 р.

Медленно выполняется запрос при join по dblink

Есть две таблицы: одна моя, другая удаленная. Связываю по праймари полю удаленной таблицы и мой запрос висит. Если напрямую обращаться к удаленной таблице с поиском по PK, то все отрабатывает быстро, в плане запроса видно, что используется primary key. Но только join и я жду.
Гугл выдает, что можно попробовать хинт driving_site. Добавляю хинт и запрос выполняется за секунду. Еще пару полезных ссылок по этой теме: раз, два.

пʼятниця, 14 квітня 2017 р.

InputBox в Delphi для ввода пароля

Что бы скрыть звездочками ввод в InputBox надо добавить маску #1 перед вторым параметром:
sPass := InputBox('Введите пароль', #1'Пароль:', '');

Вот ссылка на InputQuery, где описана данная фича, которая подходит и для InputBox. Если я правильно понял, то функционал был добавлен в версии XE.

середа, 12 квітня 2017 р.

ORA-39726 или как удалить колонку

Добавил в таблицу 2 новые колонки, а через время пересмотрел свое решение и понял, что они не нужны, и надо сделать по-другому. Пробую удалять и ... ошибка ORA-39726:
unsupported add/drop column operation on compressed tables

Лезу в гугл. Кайт и Бурлесон пишут, что надо делать так:
--помечаем колонки как неиспользуемые
alter table TBL_1 set unused column Column1;
alter table TBL_1 set unused column Column2;
--удаляем неиспользуемые колонки
alter table TBL_ROZ_SALES drop unused columns;

Судя по тому, что Том пишет, что при этой процедуре будет перезаписан весь сегмент, решил проводить операцию удаления колонок ночью. Весь процесс занял где-то минуту.

субота, 1 квітня 2017 р.

4wd robot arduino или машинка-конструктор с Китая

Заказал на алиэкспрессе вот такую машинку:

Собирал по мануалам и видео с интернета, гуглите "4wd robot arduino".
Первоначально собрал с сервоприводом и датчиком фиксации препятствий, который и был установлен на сервопривод. Код, который идет с видео сразу не работал, подкорректировал и все заработало:
#include <Servo.h>
Servo myservo;
int enableA = 1;
int pinA1 = 3;
int pinA2 = 2;
int servposnum = 0;
int servpos = 0;
int enableB = 6;
int pinB1 = 5;
int pinB2 = 4;
#define trigPin 7
#define echoPin 8
void setup() {
// put your setup code here, to run once:
//configure pin modes for the drive motors
pinMode (enableA, OUTPUT);
pinMode (pinA1, OUTPUT);
pinMode (pinA2, OUTPUT);
pinMode (enableB, OUTPUT);
pinMode (pinB1, OUTPUT);
pinMode (pinB2, OUTPUT);
//configure pin modes for the ultrasonci se3nsor
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
//Servo pins
myservo.attach(9);
}
void loop() {
// put your main code here, to run repeatedly:
car();
avoid();
}
//Defining functions so it's more easy
//motor functions
void motorAforward() {
digitalWrite (pinA1, HIGH);
digitalWrite (pinA2, LOW);
}
void motorBforward() {
digitalWrite (pinB1, LOW);
digitalWrite (pinB2, HIGH);
}
void motorAbackward() {
digitalWrite (pinA1, LOW);
digitalWrite (pinA2, HIGH);
}
void motorBbackward() {
digitalWrite (pinB1, HIGH);
digitalWrite (pinB2, LOW);
}
void motorAstop() {
digitalWrite (pinA1, HIGH);
digitalWrite (pinA2, HIGH);
}
void motorBstop() {
digitalWrite (pinB1, HIGH);
digitalWrite (pinB2, HIGH);
}
void motorAcoast() {
digitalWrite (pinA1, LOW);
digitalWrite (pinA2, LOW);
}
void motorBcoast() {
digitalWrite (pinB1, LOW);
digitalWrite (pinB2, LOW);
}
void motorAon() {
digitalWrite (enableA, HIGH);
}
void motorBon() {
digitalWrite (enableB, HIGH);
}
void motorAoff() {
digitalWrite (enableA, LOW);
}
void motorBoff() {
digitalWrite (enableB, LOW);
}
// Movement functions
void forward (int duration) {
motorAforward();
motorBforward();
delay (duration);
}
void backward (int duration) {
motorAbackward();
motorBbackward();
delay (duration);
}
void right (int duration) {
motorAbackward();
motorBforward();
delay (duration);
}
void left (int duration) {
motorAforward();
motorBbackward();
delay (duration);
}
void coast (int duration) {
motorAcoast();
motorBcoast();
delay (duration);
}
void breakRobot (int duration) {
motorAstop();
motorBstop();
delay (duration);
}
void disableMotors() {
motorAoff();
motorBoff();
}
void enableMotors() {
motorAon();
motorBon();
}
int distance() {
int duration, distance;
digitalWrite(trigPin, HIGH);
delayMicroseconds(1000);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration/2) / 29.1;
return distance;
}
void car() {
int distance_0;
distance_0 = distance();
while(distance_0 > 15)
{
if(servposnum == 0)
{
myservo.writeMicroseconds (1900);
servposnum = 1;
delay(100);
}
else if(servposnum == 1)
{
myservo.writeMicroseconds (2200);
servposnum = 2;
delay(100);
}
else if(servposnum == 2)
{
myservo.writeMicroseconds (1900);
servposnum = 3;
delay(100);
}
else if(servposnum == 3)
{
myservo.writeMicroseconds (1600);
servposnum = 1;
delay(100);
}
motorAon();
motorBon();
forward(1);
distance_0 = distance();
}
breakRobot(0);
}
void avoid()
{
backward(500);
right(360);
}

Получилось вот так:

Поигрался и решил переделать на управление по блютузу, в комплекте идет модуль. Все подключил, программу написал:
int enableA = 1;
int pinA1 = 3;
int pinA2 = 2;
int enableB = 6;
int pinB1 = 5;
int pinB2 = 4;
char state = '0';
void setup() {
//configure pin modes for the drive motors
pinMode (enableA, OUTPUT);
pinMode (pinA1, OUTPUT);
pinMode (pinA2, OUTPUT);
pinMode (enableB, OUTPUT);
pinMode (pinB1, OUTPUT);
pinMode (pinB2, OUTPUT);
Serial.begin(9600); // Default connection rate for my BT module
}
void loop() {
state = Serial.read();
if (state == 'f') {
forward(10);
}
else if (state == 'b') {
backward(10);
}
else if (state == 'l') {
left(10);
}
else if (state == 'r') {
right(10);
}
else if (state == 's') {
breakRobot(10);
}
else if (state == '1') {
enableMotors();
}
else if (state == '0') {
disableMotors();
}
}
//motor functions
void motorAforward() {
digitalWrite (pinA1, HIGH);
digitalWrite (pinA2, LOW);
}
void motorBforward() {
digitalWrite (pinB1, LOW);
digitalWrite (pinB2, HIGH);
}
void motorAbackward() {
digitalWrite (pinA1, LOW);
digitalWrite (pinA2, HIGH);
}
void motorBbackward() {
digitalWrite (pinB1, HIGH);
digitalWrite (pinB2, LOW);
}
void motorAstop() {
digitalWrite (pinA1, HIGH);
digitalWrite (pinA2, HIGH);
}
void motorBstop() {
digitalWrite (pinB1, HIGH);
digitalWrite (pinB2, HIGH);
}
void motorAon() {
digitalWrite (enableA, HIGH);
}
void motorBon() {
digitalWrite (enableB, HIGH);
}
void motorAoff() {
digitalWrite (enableA, LOW);
}
void motorBoff() {
digitalWrite (enableB, LOW);
}
// Movement functions
void forward(int duration) {
motorAforward();
motorBforward();
delay (duration);
}
void backward(int duration) {
motorAbackward();
motorBbackward();
delay (duration);
}
void right(int duration) {
motorAbackward();
motorBforward();
delay (duration);
}
void left(int duration) {
motorAforward();
motorBbackward();
delay (duration);
}
void breakRobot(int duration) {
motorAstop();
motorBstop();
delay (duration);
}
void disableMotors() {
motorAoff();
motorBoff();
}
void enableMotors() {
motorAon();
motorBon();
}

и ... ардуино не воспринимает сигналы с телефона по блютузу, машинка не едет. Разбирался с проблемой пару часов, пока в комментариях кода, в одном из китайских примеров не увидел надпись:
Rx -> Tx
Tx -> Rx
А я подключал наоборот, думал, что если написано Rx, то надо и подключать к Rx =)
В общем, поменял контакты и все заработало.
Для управления с телефона использовал программу bluetooth spp pro.

Вот некоторые фотки процесса:


































Вообще мне набор понравился, возможно 42 доллара это и много, но мне было интересно собирать "конструктор", познакомиться с Arduino и разбираться с возникнувшими проблемами.

неділя, 29 січня 2017 р.

Raspberry Pi, Pi Camera и отправка email

Вот уже как пару дней, я пользуюсь своим детектором движений на малине. Основной принцип работы, я описал в первом абзаце этого поста. Фото и видео делает Pi Camera Module. Вот статья, в которой описаны основные моменты. Питанием для всей системы я выбрал Xiaomi Mi Power Bank 16000mAh. Реальная емкость где-то на уровне 12000 мАч. Непрерывной работы от одной полной зарядки банки хватает на 24-25 часов. Значит система потребляет где-то 500мА в час.
Корпусом для мини-пк я выбрал коробку от смартфона. Прорезал в ней отверстия под питание, usb-выход и кабеля для датчика движений. В итоге, в собранном виде все это дело выглядит вот так:
На Raspberry был установлен TeamViewer и, перед каждым уходом на работу, я подключаюсь и запускаю скрипт. Для того что бы камера не фиксировала меня когда я ухожу, я поставил задержку в 1 минуту.
Привожу полный код скрипта, который сейчас благополучно работает у меня:
import picamera
import time
import RPi.GPIO as GPIO
from datetime import datetime
import smtplib
from os.path import basename
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
#send e-mail via gmail
def send_mail(send_from, password, send_to, send_to_hidden, subject, file_name):
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = send_to
msg['Subject'] = subject
with open(file_name, "rb") as fil:
part = MIMEApplication(
fil.read(),
Name=basename(file_name)
)
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(file_name)
msg.attach(part)
smtp = smtplib.SMTP('smtp.gmail.com', 587)
smtp.starttls()
smtp.login(send_from, password)
smtp.sendmail(send_from, [send_to] + [send_to_hidden], msg.as_string())
smtp.quit()
### main block ###
#number of GPIO on board
sensor = 4
GPIO.setmode(GPIO.BCM)
GPIO.setup(sensor, GPIO.IN)
delay_sec = 60
mark_time = time.time()
cur_state = 0
while True:
time.sleep(1)
cur_state = GPIO.input(sensor)
print(cur_state)
if cur_state == 1:
if time.time() - mark_time > delay_sec:
delay_sec = 10
msg = "Some movement. Time: {0}".format(str(datetime.now())[:-7])
print(msg)
mark_time = time.time()
with picamera.PiCamera() as camera:
#photo
camera.resolution = (1280, 720)
time.sleep(1)
filename = '/home/pi/Desktop/Pictures/%s.jpg' % str(datetime.now())
camera.capture(filename)
#send email
send_mail('my_email@gmail.com',
'mypass',
'first_email@gmail.com',
'second_email@gmail.com',
str(datetime.now()),
filename)
#video
# filename = '/home/pi/Desktop/Pictures/%s.h264' % str(datetime.now())
# camera.start_recording(filename)
# time.sleep(5)
# camera.stop_recording()
view raw camera hosted with ❤ by GitHub

Код по записи видео я закомментировал - мне, пока что, без надобности.

В итоге, хочу сказать спасибо людям, которые делились (и делятся) своими знаниями в Интернете касательно Raspberry Pi и всего, что его окружает. 

пʼятниця, 27 січня 2017 р.

CLOB через DBLink

Есть 2 базы, надо передавать CLOB с одной в другую. Сначала сделали процедуру, которая принимала на вход CLOB, но:
PLS-00564: lob arguments are not permitted in calls to remote server

Решили, что будем передавать CLOB в удаленную таблицу, а на той стороне обрабатывать. Написали инсерт и:
Error: PL/SQL: ORA-22992: cannot use LOB locators selected from remote tables


Решение проблемы:
Сделали у себя темповую таблицу:
create global temporary table TBL_TMP_CLOB
(
  c_clob CLOB
)


Делаем Insert From Select в удаленную таблицу:
INSERT INTO schema.remote_table@dblink(rem_clob) SELECT * FROM TBL_TMP_CLOB;





вівторок, 24 січня 2017 р.

Проблемы с Task Scheduler в Windows 10

Продолжаю допиливать свою raspberry pi-"систему видеонаблюдения". Для себя решил, что она должна работать по принципу: датчик фиксирует движение, камера делает снимок и отправляет мне на рабочую почту письмо, на работе у меня запущен шедуллер, который мониторит определенную папку в outlook и если там поменялось количество писем, то будет вызвана процедура по отправке смс мне на телефон. 
Столкнулся с проблемой простого создания задания, которое будет выполняться по расписанию.
Сразу приведу текст батника:
python webScrap1.py

Создал задание в Task Scheduler на запуск батника по расписанию в Windows 10. Жду... Ничего не запускается :(. Запускаю вручную - тоже ничего. Погуглил и нашел, что проблема может заключатся в положении переключателя "Run only when user is logged on". 
Изначально я поставил его в положение "Run whether user is logged on or not". Поменял, запустил вручную и увидел cmd-окно.
Но, хоть окно и мигнуло на секунду - ничего не произошло, смс не пришла. Поставил паузу в батнике - ошибка "не могу найти файл webScrap1.py". Прописал полный путь к файлу - cmd запускается и висит. Попробовал другие варианты - зависает пустое окно.
Методом "научного тыка" пришел к выводу, что просто указать название батника в поле с файлом, который будет запускаться не получится. Пришлось, как мне кажется, извратиться и запускать python.exe, а в аргументах указать путь к скрипту. Также надо указать папку, где лежит скрипт. Пример:

В следующих постах детальнее опишу, как будет работать вся система. Счастья, здоровья.

неділя, 15 січня 2017 р.

Futurama на дверях

Предыстория: у меня в квартире входные двери с обратной стороны белого цвета. Моя жена решила нарисовать на них картинку с космосом. Нарисовала, а потом через несколько дней все стерла, сказала, что ей не нравится. Я подумал и предложил нарисовать героев мультсериала Футурама. Поискал в интернете подходящую картинку и остановился на этой:
Решил, что нарисую только героев и только до линии плеч. Начал срисовывать. Сначала карандашом нарисовал все линии (может быть плохо видно, надо увеличить картинку):



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

В крышке замешивал цвета для более похожего цвета кожи и губ, так как телесный цвет с флакона был очень светлым. Размешал его с каплей красного и результат мне понравился.
Начал разукрашивать Бендера:

Уже в процессе понял, что надо было купить еще одну кисточку, тонкую, что бы делать обводку черным цветом. Решил проблему так: отрезал маленький кусочек картона с коробки от пиццы и макал в краску. Результат:

Дальше перешел к Фраю и Лиле:

Делаю черный контур:
Итог работы:
Конечно, я понимаю, что можно было сделать лучше: Фрай получился меньше остальных (будем думать, что он стоит дальше всех), обведение черным неровное и местами чересчур жирное (в следующий раз куплю тонкую кисточку), где-то краска выходит за контуры (буду говорить, что так и было задумано). Но так как подобным я занимаюсь первый раз, я остался доволен проделанной работой.
Всем добра и творческих успехов =)