[Решен] Как да сменям цвета на Svg икони?



  • Здравейте,
    Имам Тул-бар с много икони, но искам потребителя да може да избира големината на иконите в тулбара без ограничения. Затова се спрях на Svg икони защото са векторна графика и изглеждат добре във всякакъв размер.

    Имам обаче следната идея: всички икони да са в един цвят, като потребителя да може да избира този цвят. Който иска - сини, който иска зелени, розови дори... Но не се сещам как да променям цвета на иконите по време на изпълнение на програмата.
    В момента иконите ми са сиви, със цвят #555555 и съм ги сложил в resources файла, като ги достъпвам по следния начин:
    @btnText_ZoomIn->setIcon( QIcon(":/resources/images/zoomin.svg") );@

    Самите икони са един вид XML файлове и би могло да се достигне до различните атрибути, в случая до техния цвят и да се замени с желания, а след това да се визуализира. Или иначе казано да открия #555555 и да го заменя с да речем #a00000. Но аз не искам иконите да стоят отделно от изпълнимия файл, а искам да са вградени в него т.е. да са в resources файла. Как при това положение да "бръкна" в тях и да им сменя атрибута с цвета и то по време на изпълнение на програмата?



  • Забравих да кажа, че това което ми идва като идея, но не знам как да реализирам, е в началото - при пускане на програмата в един цикъл да обиколя всички ресурсни файлове и по някакъв начин да заменя в тях #555555 с потребителски избрания цвят (това означава, че потребителя ще избере цвят, но той ще влезне в сила след рестарт на програмата, което не е проблем - не се прави всеки ден).



  • Здравей,

    това с менкането на цветове изглежда като мръсен хак :) Мисля, че трябва да стане нещо такова:

    Зареждаш svg-то с "QSvgRenderer::load":http://doc.qt.nokia.com/latest/qsvgrenderer.html#load, създаваш си един QPixmap и QPainter от този QPixmap и даваш на *QSvgRenderer::render * . Така ще имаш картинката в пиксмапа, колоризираш я( пример: http://www.slideshare.net/qtbynokia/special-effects-with-qt-graphics-view на слайд 34 ) и след това създаваш QIcon от Pixmap-а. И си готов :))

    На теория трябва да стане така като чета документацията, но реално трябва да го пробвам, утре вечер(то вече е днеска) ако имам време :)



  • [quote author="M_3_T" date="1323036425"]Имам обаче следната идея: всички икони да са в един цвят, като потребителя да може да избира този цвят. Който иска - сини, който иска зелени, розови дори... Но не се сещам как да променям цвета на иконите по време на изпълнение на програмата.[/quote]

    Тъй като промяната на съдържанието на SVG чрез редакция и след това зареждането на SVG икони по време на изпълнение на програмата отнема ресурси и тъй като като аз съм мързелив все пак бих ти препоръчал да обмислиш и варинта с няколко готови сета от PNG икони :) Примерно 16 сета за 16 цвята, от които да избира потребителя.



  • При моят вариант можеш да оптимизираш нещата като използваш "QPixmapCache":http://doc.qt.nokia.com/4.7/qpixmapcache.html .

    Когато трябва да се зареди икона, се проверява дали тя вече не е в кеша. За ключ може да се използва името на иконата + номера на цвета. Така може да се сменя цвета дори по време на изпълнение на програмата, като изчистиш кеша.



  • В момента се боря с един друг проблем и съм зарязал цветовете на иконите.
    Имам около 40 икони, разположени в тулбарове. Всичко това се зарежда еднократно при стартиране, така че няма нужда от кеш. Нямам и менюта в които да се дублират иконите. Имам само тулбарове.

    Направих потребителя да си задава големината на иконите в тулбаровете като може да избира измежду следните големини: 16, 24, 32, 40 и 48, а самите икони съм ги направил 24x24. И тук идва проблема. Първо да кажа, че иконите са еднотипни - всички са направени от линии и кръгове с дебелина 3 пиксела. Няма 3D елементи - всичко е плоско и едноцветно. Например иконата за търсене е лупа - едно кръгче и една дръжка разположена под 45 градуса.

    И така - ако се избере 24x24, иконата се показва едно към едно и изглежда идеално.
    Ако се избере 48x48 иконите са два пъти по-големи и също изглеждат идеално. Самите елементи в тях са вече с дебелина 6 пиксела.
    Проблема идва като се избере друга големина - става едно размазване, което не мога да си обясня.

    Гореизброените големини на иконите съм ги подбрал така, че да са кратни на 8 (затова няма 20x20).
    Иконата със стандартна големина (в която не се използва zoom) е както казах 24x24, а елементите вътре да са 3 пиксела.
    Би трябвало ако се избере 32x32 иконите да се увеличат с 1/3.
    24 + (24*(1/3)) = 32
    Би трябвало по същия начин и елементите в тях да се увеличат и така за икона с размер 32x32 елементите трябва да станат с дебелина 4 пиксела и да изглеждат добре. Защо са размазани тогава?

    Написах до много сложно и не знам дали ме разбрахте.



  • Аааа проблема ми май е, че правя иконите да са кратни на 8, а то трябва да са кратни на 3... Ох...



  • Нямам идея защо така става, но мога да ти предложа нещо. Пробвай да си направиш иконите примерно 128х128. В KDE така са направили - всички размери са кратни на 8 и иконите в svg формат са 256x256 пиксела.

    Другото, което ми хрумва е, че картинката е двузимерна. На практика трябва на всяка страна да се увеличи с 1/3(примерно), но може би не се получава така в действителност...



  • [quote author="task_struct" date="1323040302"]Здравей,

    това с менкането на цветове изглежда като мръсен хак :) Мисля, че трябва да стане нещо такова:

    Зареждаш svg-то с "QSvgRenderer::load":http://doc.qt.nokia.com/latest/qsvgrenderer.html#load, създаваш си един QPixmap и QPainter от този QPixmap и даваш на *QSvgRenderer::render * . Така ще имаш картинката в пиксмапа, колоризираш я( пример: http://www.slideshare.net/qtbynokia/special-effects-with-qt-graphics-view на слайд 34 ) и след това създаваш QIcon от Pixmap-а. И си готов :))

    На теория трябва да стане така като чета документацията, но реално трябва да го пробвам, утре вечер(то вече е днеска) ако имам време :)[/quote]

    task_struct, дай да го подкараме по двата начина - хем с "мръсен хак", хем както ти казваш.
    Идеята ми е следната. Да вкарам svg иконите не като ресурси, а като стринг в cpp файла. Знам че е глупаво, но голяма част от Svg файловете са с обща информация, която може да се сложи в един метод, а конкретните картинки в други методи, които да се извикват и да връщат един голям стринг представляващ Svg картинката (с предварително променен цвят). После вкарвам Svg стринга в един QByteArray, а него подавам в конструктора на QSvgRenderer от долния пример. Щялата тази гимнастика е защото няма начин в setIcon() метода да вкарам самия файл било то като стринг или QByteArray. Мога да укажа само пътя към този файл.

    Аз направих следния неуспешен опит:

    @
    QPixmap *pixmap = new QPixmap();
    QPainter *painter = new QPainter(pixmap);
    QSvgRenderer *svgRenderer = new QSvgRenderer(QString(":/resources/images/new.svg"));
    svgRenderer->render(painter);
    ...
    btnMain_New->setIcon( QIcon(*pixmap) );
    @

    Не се появи никаква картинка, но и не даде грешка.
    Ако подкарам това нещо от ресурсен файл, после ще му дам файла като QByteArray (но да не се повтарям).
    Къде бъркам в горните редове?



  • Ще трябва да го пробвам като се прибера унас. За сега мога да предложа само някои идеи.

    1. Виж размерите на Pixmap-а да не би да го е направил с размер 0х0 като не си му задал размери. В документацията на QPainter има следният пример:

    @
    QPixmap image(0, 0);
    painter->begin(&image); // impossible - image.isNull() == true;
    @

    1. Провери размера на .svg документа. Защото примерно Inkscape прави документа с размери на А4 някъде, а картинката ти е по-малка и може да рисува в QPixmap-а някое празно поле.

    Добавка: От документацията на QPixmap:

    bq. QPixmap::QPixmap ()
    Constructs a null pixmap.

    Вероятно това е приблема. Създавай QPixmap-a с размерите, които искаш.



  • Ами да - това беше проблема - зададох размер и стана. Но се наложи и да инициализирам с прозрачност. Сега ще се захвана с моята идея и като стане ще пейстна малко код тук. Също ще замеря и за колко време рисува иконите по нормалния начин и по другия.

    А ето кода до този момент за който се интересува:

    @QPixmap *pixmap = new QPixmap(btnsIconSize, btnsIconSize);
    pixmap->fill(QColor(0, 0, 0, 0)); // Инициализира с прозрачност.
    QPainter *painter = new QPainter(pixmap);
    QSvgRenderer *svgRenderer = new QSvgRenderer(QString(":/resources/images/new.svg"));
    svgRenderer->render(painter);
    ...
    btnMain_New->setIcon( QIcon(*pixmap) );@

    Отново ще кажа - този код така както е показан е безсмислен, но когато се замени източника на иконата - да не е от ресурсите, а от стринг който е предварително модифицитан, вече има смисъл.



  • (Поста стана много дълъг, затова го пускам на части)

    Готов съм с идеята си за смяна на цвета на иконите от потребителя (по време на изпълнение на програмата). По-долу ще копирам част от кода, който работи без проблем.
    С няколко думи ще повторя каква ми беше идеята и как я реализирах, а самия код ще покаже подробностите.
    Имам над 40 икони разположени в тулбарове, като искам всички те да са едноцветни (омръзна ми тая шарания), но потребителят да може сам да избира какъв да е цвета на иконите. Освен това ще предвидя и автоматичен избор, който по подразбиране да прави тъмносиви икони, ако прозорците са светли или светлосиви икони, ако прозорците са тъмни или черни (т.е. според темата). Ако пък потребителя иска да си направи иконите зелени, бембени или розови - няма проблем. Ще предвидя и отделна настройка на цвета на различните групи икони, така че все-пак да не е съвсем едноцветно. Но това са подробности - само идеи.

    Самата идея да се направи "он-лайн" смяна на цвета на иконите ме доведе до заключението, че не мога да използвам стандартните PMG или JPG икони, а такива които се описват в XML файл и са достъпни за манипулиране от програмата. Идеалният вариант е SVG формата, който е векторна графика описана в текстов (XML) файл. До тук добре, но как да променя цвета в този файл, преди да го изрисувам на бутона? Оказа се по-трудно отколкото очаквах, но в крайна сметка се намери решение. Правят се няколко гимнастики и става. Самите примери по-долу са достатъчно ясни, а и има коментари, затова няма да повтарям какво съм направил.

    Направих обаче и една проверка за времето. Значи: 46 икони, по стандартния начин - от ресурсен файл, се изчертават за между 63 и 47 msek. Най-често за 49 msec.
    По новия начин се изчертават за между 69 и 31 msek. Най-често за 39 msec.
    За моя изненада, оказа се че по новия начин иконите се изчертават по-бързо отколкото от ресурсен файл. При това с включена модификация.



  • Не е нужно да напомням, че по този метод може да се модифицира не само цвета, но и съдържанието на иконата. Например да се изписва някакво число (обърнете внимание, че когато QtCreator изпише грешки при компилация, долу в иконата му се изписва броя на грешките. Предполагам че се прави по подобен начин).

    Трябва да уточня и че по този начин иконите се рисуват с Inkscape, но те не влизат в ресурсния файл на проекта - просто си стоят в директорията, а чрез трудоемки копи-пейст операции се копират в статичните методи показани долу. Една голяма част от SVG файла е еднаква за всички икони, което ме накара да я изнеса в един общ метод, а в специфичните методи правя сглобката между тази обща част и специфичната част. Това води до важното условие: Направете една икона-шаблон - дори да е празна и после от нея правете всички икони. Това ще гарантира общата им част. Освен това голяма част от тази обща част изобщо не е нужна, но още не съм си правил труда да отстраня ненужните редове, а и това май няма никакво значение, щом няма проблем със скоростта на изчертаване.

    Това че аз правя едноцветни икони, не значи че не могат да се направят например 5 цветни икони и потребителя да избира сам 5-те цвята. Става много яко! Ще напомня и че QColor предлага метод (само да не се лъжа) с който може да се направи даден цвят да бъде X пъти по-светъл или по-тъмен от зададения. Така може да се направят и светлосенки, като потребителя избира основния цвят, а сянката става автоматично. Всъщност сянка може да се получи и ако се изчертае с потребителския цвят, но с променена степен на прозрачност (за пример - червеното: QColor(255, 0, 0, 128) - 128 ще направи червеното да е полу-прозрачно, а ако се направи още по-прозрачно и ето ти сянка с оттенък на потребителски избрания цвят).

    На края искам да кажа, че открих и тайната на запазване на качеството на иконите при мащабиране, но мисля да напиша отделна тема или Wiki за това.



  • (Като сложа тагове за код и част от кода не се показва, - почти цялото съдържание на метода Icon_Close не се вижда. Защо става така? В превюто всички е наред. Ако искате да го видите натиснете бутона за писане на коментар „quote“ и си копирайте кода някъде)

    @#ifndef SVGICONS_H
    #define SVGICONS_H

    #include <QObject>
    #include <QPixmap>
    #include <QIcon>
    #include <QtSvg>

    class SvgIcons
    {
    private:
    static QString GeneralSvgPart();
    static QPixmap GeneratePixmap(int width, int height, QString *lastSvgPart);

    public:
    //FileToolBar
    static QIcon Icon_Close(int width, int height, QColor color);
    static QIcon Icon_PrintPreview(int width, int height, QColor color);
    static QIcon Icon_Print(int width, int height, QColor color);
    ...
    //HelpToolBar
    static QIcon Icon_Help(int width, int height, QColor color);
    static QIcon Icon_Info(int width, int height, QColor color);
    ...
    };

    #endif // SVGICONS_H
    @

    @
    #include "svgicons.h"

    QString SvgIcons::GeneralSvgPart()
    {
    return QString(
    "<?xml version="1.0" encoding="UTF-8" standalone="no"?>"
    "<!-- Created with Inkscape (http://www.inkscape.org/) -->"
    "<svg"
    " xmlns:dc="http://purl.org/dc/elements/1.1/""
    " xmlns:cc="http://creativecommons.org/ns#""
    " xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#""
    " xmlns:svg="http://www.w3.org/2000/svg""
    " xmlns="http://www.w3.org/2000/svg""
    " xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd""
    " xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape""
    " width="24""
    " height="24""
    " id="svg2""
    " version="1.1""
    " inkscape:version="0.48.0 r9654""
    " sodipodi:docname="noname.svg">"
    " <defs"
    " id="defs4" />"
    " <sodipodi:namedview"
    " id="base""
    " pagecolor="#ffffff""
    " bordercolor="#646464""
    " borderopacity="1.0""
    " inkscape:pageopacity="0.0""
    " inkscape:pageshadow="2""
    " inkscape:zoom="16""
    " inkscape:cx="11.946628""
    " inkscape:cy="14.556665""
    " inkscape:document-units="px""
    " inkscape:current-layer="layer1""
    " showgrid="true""
    " inkscape:showpageshadow="false""
    " inkscape:window-width="1280""
    " inkscape:window-height="962""
    " inkscape:window-x="-8""
    " inkscape:window-y="-8""
    " inkscape:window-maximized="1""
    " inkscape:snap-nodes="false">"
    " <inkscape:grid"
    " type="xygrid""
    " id="grid2999" />"
    " </sodipodi:namedview>"
    " <metadata"
    " id="metadata7">"
    " rdf:RDF"
    " <cc:Work"
    " rdf:about="">"
    " dc:formatimage/svg+xml</dc:format>"
    " <dc:type"
    " rdf:resource="http://purl.org/dc/dcmitype/StillImage" />"
    " <dc:title />"
    " </cc:Work>"
    " </rdf:RDF>"
    " </metadata>"
    " <g"
    " inkscape:label="Layer 1""
    " inkscape:groupmode="layer""
    " id="layer1">");
    }

    QPixmap SvgIcons::GeneratePixmap(int width, int height, QString *lastSvgPart)
    {
    QPixmap pixmap(width, height); // Това е картинката която ще се получи и използва като икона.
    pixmap.fill(QColor(0, 0, 0, 0)); // Инициализира я с прозрачност (последната нула).

    QPainter painter(&pixmap);

    QByteArray ba; // Този QByteArray се използва само защото QSvgRenderer може да приеме Svg картинка от стринг само чрез QByteArray.
    ba.append(GeneralSvgPart() + *lastSvgPart); // Това е модифицираната Svg картинка.

    QSvgRenderer svgRenderer(ba); // Зарежда модифицираната Svg картинка.
    svgRenderer.render(&painter); // Чрез QPainter я преобразува в QPixmap.

    return pixmap;
    }

    === СЪДЪРЖАНИЕТО НА ТОЗИ МЕТОД ПОРАДИ НЕРАЗБИРАЕМА ПРИЧИНА НЕ СЕ ИЗПИСВА - МНОГО РЕДОВЕ ЛИПСВАТ!!!
    QIcon SvgIcons::Icon_Close(int width, int height, QColor color)
    {
    QString lastSvgPart = QString(
    " <path"
    " style="fill:none;stroke:%1;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none""
    " d="m 1.5,1.5 21,21 m 0,-21 -21,21""
    " id="path2984""
    " inkscape:connector-curvature="0""
    " sodipodi:nodetypes="cccc" />"
    " </g>"
    "</svg>").arg(color.name());

    return QIcon( GeneratePixmap(width, height, &lastSvgPart) );
    }
    @

    @
    int btnsIconSize = 24; // 24x24
    QColor btnsIconColor = QColor(102, 102, 102); // Цвят на всички икони (в този пример иконите са едноцветни).

    btnFile_Close = new QToolButton(panelButtons);
    btnFile_Close->setIconSize(QSize(btnsIconSize, btnsIconSize));
    //btnFile_Close->setIcon( QIcon(":/resources/images/close.svg") ); // Стандартен метод за изчертаване на икона от ресурс.
    btnFile_Close->setIcon( SvgIcons::Icon_Close(btnsIconSize, btnsIconSize, btnsIconColor) ); // Новия метод за изчертаване на модифицираната икона.
    @



  • Здравей,

    поздравления за успеха :)

    Бих ти предложил да използваш "QLatin1String":http://developer.qt.nokia.com/doc/qt-4.8/qlatin1string.html за стрингове, които съдържат само латински символи. Това ще ти спести памет, защото QLatin1String съхранява данните си в ASCII (8бита), а QString копира и конвертира всичко в UTF-8 ( 16 бита )

    Също е добра практика да използваш const където е възможно :)



  • Е той успеха дойде чрез вас двамата, така че благодарности на вас! :)
    Ако благодарностите можеха да се ядат, щяхте да сте доста дебели :D
    Веднага ще коригирам каквото ми каза (за QLatin1String не го знаех).

    Забравих да кажа, че за да се използват SVG икони в проектния файл трябва да се добави:

    @QT += svg@

    Също така, за Windows в директорията на exe-то трябва да се добави директория Plugins, в нея директория iconengines и в нея файла qsvgicon4.dll, който се взима от подобната директория в Qt SDK-то.
    После в main.cpp файла трябва да се напише:

    @app.addLibraryPath(QCoreApplication::applicationDirPath() + "/Plugins");
    app.addLibraryPath(QCoreApplication::applicationDirPath() + "/Plugins/iconengines");@

    Ако това не се направи, няма да покаже никаква грешка, но иконите няма да се изчертават, което ми е още по-непонятно. Поне да изкарваше някакво съобщение.

    Подобно нещо се прави и ако се използва Phonon.

    Представа си нямам какво е това и защо се прави за Windows и защо не се прави за Линукс. Някой може ли да ми разясни?



  • Значи, това дето го говорех по-горе за прозрачностите нещо не става. Като задам цвят с прозрачност:
    @additionalColor = QColor(generalColor);
    additionalColor.setAlpha(128);@

    или дори така:

    @QColor btnsIconColor = QColor(102, 102, 102, 128);@

    ...не става. SVG-то нещо не възприема прозрачности или нещо аз не правя като хората.

    Обаче става с онези две функции:
    @
    //additionalColor = generalColor.darker(120);
    additionalColor = generalColor.lighter(120);@

    Чрез тях може потребителски зададения цвят (generalColor) да се потъмни или осветли и да се подаде като допълнителен цвят (additionalColor) на някои от детайлите на иконите и да стане по-разчупено, без да е шарения.

    QLatin1String пък няма .arg() и става по-сложно да го използвам, а за тези икони паметта не е кой-знае каква че да я пестя.



  • [quote author="M_3_T" date="1324252783"]Значи, това дето го говорех по-горе за прозрачностите нещо не става. Като задам цвят с прозрачност:
    @additionalColor = QColor(generalColor);
    additionalColor.setAlpha(128);@

    или дори така:

    @QColor btnsIconColor = QColor(102, 102, 102, 128);@

    ...не става. SVG-то нещо не възприема прозрачности или нещо аз не правя като хората.
    [/quote]

    Голяма глупост съм направил с тези прозрачности. Ама нищо - така се учи човек...
    Прозрачностите в SVG иконите се прави по съвсем друг начин. Пробвах го и става. В самата SVG икона (в XML-а) се прави:

    @stroke-opacity:1;@
    и/или
    @fill-opacity:1;@

    като 1 означава без прозрачност, а по-малко от 1 (между 0 и 1) е някаква степен на прозрачност.

    Ето един пример (дано да се изпише коректно) с полу-прозрачност (stroke-opacity:0.5;):

    @style="fill:none;stroke:#646464;stroke-opacity:0.5;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"@



  • Днес в пощенския списък получих серия от писма свързани с SVG. Изглежда че SVG модула е обявен за "Deprecated":

    bq. That's the "Done" state.
    QtSvg was in the "Deprecated" camp for two reasons:

    1. there's a more complete implementation of SVG inside WebKit
    2. QtSvg says it implements SVG Tiny 1.2
      The second item is important: we can't consider done if we haven't achieved
      full compliancy. Not to mention that any bugs related to being compliant would
      need to be fixed.
      That's why it ended up in Deprecated: we actually want people to stop using
      the module.

    Щял да се поддържа само в WebKit, което ще рече заради едни SVG икони да добавя 20MB към exe-то си. Това е нечувано!

    Ама още по-фрапиращо е едно друго нещо което прочетох:

    bq. With Nokia about to be bought by Microsoft (rumors, but I see it as inevitable) I don't think trolls have a future working on anything not Microsoft anyway. If bought I'd expect Qt5 to be canceled unless the community delivers it.



  • По SVG-то, нямам идея, но 20МБ за сеагшните системи не са кой знае какво. Мисля, че с някои оптимизации на линкера, повечето от кодадаже няма да се зареди, защото не се извиква.

    За Qt5, мога да кажа, че ще има. М$ искат само смартфоните на Нокия, на ниският клас БозаОС не може да тръгне, защото е прекалено тежка (изненада), а и предоставя прекалено много възможности, от които там не са нужни. Даже напоследък дочух, че може би все пак ще има версия на Qt за WP.

    Дискусии по темата: http://developer.qt.nokia.com/forums/viewthread/2675
    и http://developer.qt.nokia.com/forums/viewthread/12513 Но не съм ги чел.



  • [quote author="task_struct" date="1326185085"]Даже напоследък дочух, че може би все пак ще има версия на Qt за WP.[/quote]

    Offtopic: А, дано!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.