0,0
рейтинг
26 сентября 2012 в 18:26

Разработка → Редизайн Qt Creator своими руками из песочницы

Qt*


Многие из тех кто занимаются разработкой на C++/Qt знакомы с такой средой как Qt Creator, создатели которой потрудились над дизайном не меньше чем над функциональностью. Но меня, как любителя темных цветовых схем и плоского минимализма, всегда не устраивали светлый фон панелек и градиентные заголовки.

Казалось бы, открытый исходный код — бери да меняй, но неопытность и лень останавливали меня, пока я не узнал про такую вещь как Qt Style Sheets, позволяющюю описать вид виджетов в формате css.



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

Подготовка среды


UPD: Ничего патчить ненадо Вместо этого по совету cyberbobs достаточно запускать QtCreator с параметром -stylesheet=stylesheet.css, поэтому сразу переходим к перерисовке, но если очень хочется

то продолжайте
Для начала берем исходный код среды. Распаковываем и добавляем в конструктор MainWindow::MainWindow() расположенный примерно в ./src/plugins/coreplugin/mainwindow.cpp:199 свой костыль, снабдив его опознавательными знаками, чтоб в случае чего можно было быстро найти и уничтожить:
//$$MARKER
//HACK: Injecting css to change appearance

//Получаем путь к папке с файлами приложения
//В Linux: /home/shed/.local/share/data/Nokia/QtCreator
//В Windows: C:\Document and Settings\user\Local Settings\Application Data
QString csspath = QDesktopServices::storageLocation(QDesktopServices::DataLocation)+"/stylesheet.css";
QFile css(csspath);
if (css.open(QIODevice::ReadOnly | QIODevice::Text)){
    qDebug() << "NOTE: stylesheet loaded from" << csspath;
    QString style = QTextStream(&css).readAll();
    qApp->setStyleSheet(style);
} else {
    qDebug() << "NOTE: stylesheet not found in " << csspath;
}
//$$MARKEREND

ленивые могут взять измененный файл для последней стабильной версии 2.5.0
затем
qmake && make && ./bin/qtcreator
если все прошло гладко на выходе получаем
NOTE: stylesheet not found in <путь-к-таблице-стилей>/stylesheet.css

теперь создаем этот самый stylesheet.css и пишем туда для проверки
background: blue; color: red;

перезапускаем qtcreator (для экономии нервов следовало бы настроить редактор на запуск qtcreator одним нажатием) и видим такую феерию:

Как и следовало, setStyle покрасил все в синий, но появилось два отщепенца: список классов и методов текущего документа и переключатели панелей вывода. У меня есть два предположения почему так: либо эти элементы не являются потомками QWidget, что врятли, либо они используют свой способ отрисовки обходящий систему стилей Qt, что вполне возможно, учитывая их нестандартный вид.


Перерисовка


Как известно таблица стилей представляет собой набор записей вида:

	<Селектор>[, <Селектор>, ...] {
		<Параметр>: <Значение>;
		<Параметр>: <Значение>;
		...
		<Параметр>: <Значение>;
	}

Если вы никогда раньше не писали ничего подобного, несколько уроков из любого тьюторала дадут вам представление о том что будет происходить ниже. Позже стоит прочесть документацию по Qt Style Sheet и примеры.


Селекторы


В демонологии чтобы вызвать демона нужно знать его имя, в нашем случае для составления селектора нужно знать название класса, objectName или значение любого свойства, заданного с использованием Q_PROPERTY() и setProperty().
Qt потдерживает все СSS2-селекторы. Самые полезные согласно документации:


Универсальный селектор * Соответствует всем виджетам.
Селектор типа QPushButton Соответствует экземплярам класса QPushButton и его подклассов.
Селектор свойства QPushButton[flat=«false»] Соответствуют экземплярам класса QPushButton, которые не являются плоскими (flat). Можно использовать этот селектор для проверки любого свойства Qt, заданного с использованием Q_PROPERTY().
Вместо = вы можете также использовать ~= для проверки содержит ли свойство Qt типа QStringList заданную строку QString.
Селектора класса .QPushButton Соответствует экземплярам класса QPushButton, но не его подклассов.
Эквивалентно выражению *[class~=«QPushButton»].
Селектор идентификатора (ID) QPushButton#okButton Соответствует всем экземплярам класса QPushButton, чье objectName равно okButton.
Селектор потомков QDialog QPushButton Соответствует всем экземплярам класса QPushButton, которые являются наследниками класса QDialog (дочерними, внучатыми и т.д.).
Селектор дочерних элементов QDialog > QPushButton Соответствует всем экземплярам класса QPushButton, являющихся непосредственными потомками QDialog.

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


Теперь давайте поиграем в дизайнеров.

Напомню, мы хотели сделать темный фон у панелей. Для этого нам нужно выбрать селектор. Что мы обычно видим в этих панельках? Деревья в «Проекты», «Обзор Классов», «Иерархия типов» и «Контур» (в девичестве «Outline»), списки в «Файловая Система», «Открытые Документы» и «Закладки» и таблицы в панелях отладки, т.е. стандартные QListView, QTreeView и QTableVeiw имя которым QAbstractItemView.
Поэтому напишем в наш stylesheet.css следующее:

QAbstractItemView {
	color: #EAEAEA;
	background: #232323; 
	font-size: 9pt;
}

Запускаем, уменьшаем размеры окна до минимума чтоб вылезло побольше всяких элементов и видим:

Мы получили что хотели, но наши (пусть и нелюбимые) панельки потеряли свой вид, и причем без нашей команды. Если кто пробегался глазами по MainWindow::MainWindow() возможно заметил неприметную строчку qApp->setStyle(new ManhattanStyle(baseName));, особо настойчивые могли щелкнуть по ManhattanStyle и заметить что он наследуеться от QProxyStyle т.е. именно он переопределяет рисовку у наших панелек. И именно он уходит за кулисы как только мы задаем стиль.
Предчуствуя кучу мелкой работы по наведению красоты в этих поблекших кнопках я решил не мелочиться, и вбухал помимо QAbstractItemView еще и QMainWindow — отца всех виджетов:

QAbstractItemView, QMainWindow {
	color: #EAEAEA;
	background: #232323; 
	font-size: 9pt;
}

Запускаем:

Почти то что надо. Остается омрачить белые пятна табов, хедеров и скролбаров.

Субэлементы и состояния


Как и СCS2, Qt Style Sheet поддерживает субэлементы и состояния, т.е. запись селектора в виде:
<Селектор>::<Субэлемент>:<Состояние>

Например QScrollBar::left-arrow:horizontal выбирает все левые стрелки гоизонтальных полос прокрутки.
Рассмотрим как это работает на наших белых пятнах.

Оформляем QTreeView и QAbstractItemView


Для начала изменим облик выделенного элемента в QAbstractItemView на более темный с помощью:
QAbstractItemView::item:selected {
	color: #EAEAEA;
	background-color: #151515;
}
	

Сравниваем:


Теперь разберемся с QTreeView.
Каждая его строчка состоит из одного субэлемента ::item и одного или нескольких ::branch:

::branch помимо стандартных состояний поддерживает еще 4:
*Синие — элементы с искомым состоянием
:open :adjoins-item :has-children :has-subling

Подумав, я решил забыть про надоевшие стрелочки и сделать напротив сгрупированных элементов маленькую серенькую точку. Стало быть нам нужны :closed:adjoins-item:has-children. Подергав параметры получаем чтото вроде:

QTreeView::branch:closed:adjoins-item:has-children {
     background:  solid #777777;
     margin: 6px;
     height: 6px;
     width: 6px;
     border-radius: 3px;
 }
	



Если же вы любите стрелочки, вам должна понравиться конструкция url(filename), которая переданная в image: или border-image: установит в качестве фона или границы изображение, хранящееся на жестком диске либо в системе ресурсов Qt.

Изменяем QScrollBar


В глазах Qt Style Sheets, стандартная полоса прокрутки состоит из следующих субэлементов:

Цинично превращаем это трехмерное великолепие в унылую серо-серую полоску, а кнопки вместе со стрелочками отправляем на награждение премии «Ненужно 2012» следующими строками:

QScrollBar {
     border: none;
     background: #494949;
     height: 6px;
     width: 6px;
     margin: 0px;
}

QScrollBar::handle {
     background: #DBDBDB;
     min-width: 20px;
     min-height: 20px;
}

 QScrollBar::add-line,  QScrollBar::sub-line {
     background: none;
     border: none;
}

 QScrollBar::add-page, QScrollBar::sub-page {
     background: none;
}
	

Получаем:



Модифицируем QTabBar


Без лишних слов приступаем к переделке нашей гостинной вкладок. Уж кого, а этот виджет разработчики не обделили:
  • Субэлементы: ::tear (разделитель вкладок) и ::scroller (кнопка прокрутки)
  • Целая россыпь дополнительных состояний вкладок: :only-one, :first, :last, :middle, :previous--selected, :next-selected, :selected, назначения которых я надеюсь понятны из названий.
  • Псевдо-состояния :top, :left, :right, :bottom зависимые от ориентации панели.
  • Отрицательные поля которые можно использовать для создания перекрытий

Есть где разгуляться фантазии.
Следует помнить лишь одно, QTabBar лежит на QTabWidget, поэтому фон стоит изменять через виджет, а свойства вкладок уже через панель. Но мы люди неприхотливые, к тому же о фоне у нас позаботился QMainWindow, поэтому мы ляпаем тоненькие неприметные вкладки строками:

QMainWindow, 
QAbstractItemView,  
QTreeView::branch, 
QTabBar::tab
{

...

QTabBar::tab:selected {
      font: bold;
     border-color: #9B9B9B;
     border-bottom-color: #C2C7CB; 
 }

QTabBar::tab:!selected {
     margin-top: 2px;
 }
	



Изменяем QHeaderView


Для тех кто не знал, все эти заголовки таблиц в Qt это и есть QHeaderView. У него один субэлемент ::section который обладает теми же состояниями что и QTabBar::tab.

На этот раз я решил отступиться от традиции и сделать градиентную заливку (да здесь и так можно). Для этого используются конструкции qlineargradient, qradialgradient, qconicalgradient. Градиенты указываются в режиме ограничивающего прямоугольника объекта (Object Bounding Mode). Представьте себе прямоугольник, в котором визуализируется градиент, верхний левый угол которого находится в (0, 0), а нижний правый угол — в (1, 1). Параметры градиента в этом случае указываются как доля между 0 и 1. Эти значения экстраполируются на реальные координаты прямоугольника во время выполнения. Возможно задание значений, которые лежат вне ограничивающего прямоугольника (например, -0.6 или 1.8).
Я использовал следующее оформление:

QHeaderView::section {
     background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                       stop:0 #616161, stop: 0.5 #505050,
                                       stop: 0.6 #434343, stop:1 #656565);
     color: white;
     padding-left: 4px;
     border: 1px solid #6c6c6c;
}
	


То чего мы и добивались.

Уговариваем упрямую панельку


Осталась только одна маленькая проблемка:

Этот отщепеныш остался абсолютно равнодушен к нашим стараниям, более того он оставался равнодушен даже когда мы в качестве селектора указали прадедушку QWidget'а. Несмотря на это, я все же перебирал разные селекторы. Впервые удалось пронять ее селекторами с QComboBox и QLabel:

QComboBox, QComboBox::drop-down {
     color: #EAEAEA;
     background: #232323; 
     font-size: 9pt;
     border: none;
     padding: 1px 18px 1px 3px;
     min-width: 6em;
 }

QLabel {
     border-style: solid;
     color: #EAEAEA;
     background: #232323; 
     font-size: 9pt;
}
	



Минусы на лицо. Мало неумолимой никакими border-style рамки вокруг отщепеныша, так еще и тяжелая наследственнасть просочилась везде куда ненадо.

Тут как кстати, проявились капли здравого смысла. Дети, что такое тоненькое, в верху окна с кнопочками в ряд? Конечно же это QToolBar!
Пробуем:

QToolBar {
     border-style: solid;
     border-style: outset;
     color: #EAEAEA;
     background: #333333; 
     font-size: 9pt;
}



Бинго! Пропали все следы тяжелой болезни и отщепенец послушно влился в общий вид.


Итоги



stylesheet.css

TODO:


Правильнее было бы не кодировать цвета, а брать их из палитры, которая в лучшем случае могла бы заполняться согласно цветовой схеме редактора. Кроме того можно было бы оформить все это в виде корректного дополнения и добавить свою страницу настройки с выбором стиля, и возможно редактором. Ну самое желанное это конечно же похудеть удобную но толстую боковую панелька. убрав текст и получить доступ к ее оформлению из нашего css файла, но это требует более глубокого вмешательства в код да и вообще совсем другая история.
Мальцев Владислав @shedward
карма
11,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (21)

  • +3
    Отличный пример пособия по QSS,
    но, конечно, надо понимать, что само решение в таком виде — грязный хак
    и если есть желание интегрировать это все в официальные релизы, нужно проделать очень много работы.
    А начинание хорошее, много кому, и мне в том числе, не хватает цветовых схем для панелей.
    • +1
      Ну если подобное и сливать в официальный релиз, то не в таком виде,
      все таки немало артефактов дает грубое применение css к приложению с довольно нетривиальным интерфейсом.
      Это скорее уж очень простой местячковый способ поменять цвета, чтоб глаза не мозолило.
      • +1
        О чем я и говорю, было бы здорово, если бы вы довели начатое до конца ;)
        • +1
          Я у себя дома на днях начал ковырять эту тему в коде креатора. Но работы там прилично.
    • 0
      почему грязный хак? для qt приложений предусмотрен запуск с применением qss стилей. qt creator — qt приложение. вопрос скорее не о «хак / не хак», а о корректности данного qss стиля.
  • +4
    Тут любящий QSS коллега передаёт, что достаточно запускать Qt Creator с параметром командной строки -stylesheet=stylesheet.css для достижения аналогичного результата. И не нужно ничего патчить.
    • +1
      О а вот за это большое спасибо.
    • 0
      У меня эта опция не работает. Выдает «неизвестная опция stylesheet=stylesheet.css», дальше показывает окно со списком опций Qt Creator, в списке такой опции тоже нет. QT Creator 2.4.1 из последнего QtSDK…
      • 0
        Я запускаю так:
        qtcreator -stylesheet='.qt-stylesheet.css'
        Проверьте, не забыли ли дефис или экранировать спец символы.
      • 0
        Точно такая же ситуация была (windows), исправил так:

        [pathToQt]\QtCreator\bin\qtcreator.exe -stylesheet [pathToStyleSheet]
        • 0
          Так (без знака =) этот stylesheet.css открывается в creator'e для редактирования:) А со знаком = все равно ругается на неизвестную опцию.
          Путем гугления нашел вариант, который частично работает: qtcreator.exe — -stylesheet=stylesheet.css
          (то есть два дефиса, пробел, дефис).
          • 0
            Да, открывается, но стиль всё же применяется. Я просто закрываю его и открываю проект.
  • 0
    Может кто-нибуть знает, как сделать, чтобы autocomplete появлялся сразу, а не с задержкой?
  • 0
    Действительно интересная возможность, но
    Но меня, как любителя темных цветовых схем и плоского минимализма, всегда не устраивали светлый фон панелек и градиентные заголовки.

    Вот это я не вполне понял. Qt Creator уважает системную палитру. И если установлена тёмная схема, то фон в Qt Creator тоже будет тёмным. Проверено.
    • 0
      Все верно, обычно я ставлю темную цветовую схему по всей системе, но перепробовав различные варианты, ни один не вписался по моим вкусам в окружение Unity, опять же иногда попадаются надписи черным по черному, в программах не считающихся с системной палитрой,
      поэтому я так и остался на стандартной, и если во всем интерфейсе черное на белом вполне привычно, то в редакторе кода после vim'a, Sublim'a и фильмов про хакеров, мой мозг хуже принимает цветные блоки текста на светлом фоне.
      Конечно можно поставить Vim(dark) в расцветках редактора и успокоиться,
      но я решил все к одному виду привести (а заодно и посмотреть QSS)
      • 0
        А ну и конечно, сделать панельки плоскими давно хотел, а этого даже темной цветовой схемой не добьешся
  • 0
    Действительно интересная возможность, но
    Но меня, как любителя темных цветовых схем и плоского минимализма, всегда не устраивали светлый фон панелек и градиентные заголовки.

    Вот это я не вполне понял. Qt Creator уважает системную палитру. И если установлена тёмная схема, то фон в Qt Creator тоже будет тёмным. Проверено.
    • 0
      Дико извиняюсь, то ли интернет, то ли браузер протупил. Страницу обновил, комментария не было. Отправил, а их два. Удалить нельзя, насколько я знаю? Ну, разве что НЛО подсобит…
  • 0
    Класс! Только вчера думал о том, как можно было бы решить эту проблему малой кровью, и тут — ваша статья. Хоть и «грязный хак», но для использования в личных целях, пожалуй, вполне годное решение. Спасибо!
  • 0
    Спасибо. Но было бы здорово, если бы кто-то исправил исходники, чтобы он брал текущую системную тему.
  • 0
    У кого то с последним QT creator'ом работает пример из итогов?
    У меня с этим стилем text editor вечно белого фона, даже если поставить тему Vim(dark).

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.