Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Общие вопросы _ Секреты и интересные возможности Qt

Автор: void* 23.7.2008, 22:28

Предлагаю в данную тему скидывать все известные вам интересные фичи и "секреты" Qt. Думаю, так можно собрать неплохую коллекцию интересных возможностей Qt, где каждый сможет найти что-нибудь интересное для себя или узнать новое. Начну сам:
1) Макрос Q_UNUSED позволяет избежать предупреждений по поводу неиспользования какой-то переменной. Пример:

void someFunc(int var) {
     Q_UNUSED(var); //тут мы избавлемся от возможного предупреждения от компилятора
}

2) Функция QString Qt::escape(const QString &plain) позволяет конвертировать в строке, задаваемую параметром plain метасимволы HTML (<, >, &) в их HTML-эквиваленты. Пример:
QString plain = "#include <QtCore>"
QString html = Qt::escape(plain);  // html == #include <QtCore>

3) Макрос Q_PROPERTY позволяет в некоторых случаях избежать приведения. Пример:
class SomeClass : public QObject {
     Q_OBJECT
     Q_PROPERTY(bool prop READ prop WRITE setProp);
private:
     bool var;
public:
     SomeClass() : var(true) { }
     bool prop() const { return var; }
     void setProp(bool newValue) { var = newValue; }
};
//а теперь, имея указатель на QObject, мы можем изменять значения переменных унаследованного класса:
SomeClass *sc = new SomeClass;
QObject *obj = sc;
obj->setProperty("prop", true); //тоже самое, что и sc->setProp(true);


to be continued...

P.S. Надеюсь тема не умрет, и каждый будет ее поддерживать своими находками :)

-----------------------
Ссылки на интересные возможности:
1) http://www.forum.crossplatform.ru/index.php?showtopic=2030
2) http://www.forum.crossplatform.ru/index.php?showtopic=2032

Автор: AD 24.7.2008, 9:29

P.S. Мне кажется тему можно вверху прикрепить! :)

Не знаю, возможно, это и известный факт, но мне показалось не совсем обычным такое применение функции read/write. При этом применении она становится аналогом функций Windows Api ::ReadFile/::WriteFile:

    _str strIn;
    QFile f1("nt.dat");
    f1.open(QIODevice::WriteOnly);
    f1.write( (const char *)&strIn, sizeof( strIn));
    f1.close();

    _str strOut;
    strOut.n = .0123;
    strOut.s = "test";
    strOut.y = 1;

    QFile f2("nt.dat");
    f2.open(QIODevice::ReadOnly);
    f2.read( (char *)&strOut, sizeof( strOut));
    f2.close();

В данном коде, тип _str - это пользовательский тип. В функциях read/write считываются в эту структуру данные указанного типа при преобразовании в const char*, а не в void*!

Автор: Litkevich Yuriy 24.7.2008, 9:36

void*, для 1-го пункта часто имя переменной коментируют, чтоб компиллер не ругался:

void someFunc(int /*var*/) {
     ...
}

Автор: void* 24.7.2008, 10:09

LitkevichВ Yuriy, приколупался :) лучше бы продолжил идею :)

Автор: Tonal 24.7.2008, 10:16

Цитата(AD @ 24.7.2008, 13:29) *
Не знаю, возможно, это и известный факт, но мне показалось не совсем обычным такое применение функции read/write. При этом применении она становится аналогом функций Windows Api ::ReadFile/::WriteFile:

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

Автор: AD 24.7.2008, 10:33

Цитата(Tonal @ 24.7.2008, 11:16) *
Оно вполне обычное - сохранение буфера.
Но мне кажется, код у тебя не правильный.
Если сохранение/восстановление происходят в разных программах, или даже при разных запусках одной программы, в поле strOut.s с большой вероятностью будет мусор.

не совсем понял почему неправильный. Но он исправно работает. Проверял на нескольких машинах! ;)

Автор: ЙаМайскЫйПчОЛ 24.7.2008, 11:12

Цитата(AD @ 24.7.2008, 10:29) *
Не знаю, возможно, это и известный факт, но мне показалось не совсем обычным такое применение функции read/write. При этом применении она становится аналогом функций Windows Api ::ReadFile/::WriteFile:

Это обычно для программирования под *nix'ы read/write можно применить для любого дескриптора, в т.ч. для дескриптора файла, а файл в *nix'ах это всё что есть и может быть в системе. )))

Автор: Tonal 24.7.2008, 11:48

Цитата(AD @ 24.7.2008, 14:33) *
Цитата(Tonal @ 24.7.2008, 11:16) *
...в поле strOut.s с большой вероятностью будет мусор.

не совсем понял почему неправильный. Но он исправно работает. Проверял на нескольких машинах! ;)

1) Как объявлено поле _str.s? Как char* ?
2) На что указывает поле strIn.s перед сохранением (где живёт массив символов)?
3) Как ты думаешь, что именно сохраняется в файле на месте соответствующем полю _str.s?
4) Что будет находится в поле strOut.s после чтения?
5) Куда будет указывать поле strOut.s в другой программе?
Или в этой же, если выполнится например вот такой код:
char* buf = new char[10];
_str strIn;
strIn.s = buf;
f1.write( (const char *)&strIn, sizeof( strIn));
delete[] buf;
_str strOut;
f2.read( (char *)&strOut, sizeof( strOut));
cout<<strOut.s<<endl; //???


P.S. В своём первом посте, ты strOut и strIn местами не перепутал? :)
P.P.S Если я ошибся в первом предположении, и поле _str.s имеет тип строки (QString, или std::string, или ещё какой), задумайся, как он устроен, и где хранятся те символы, которые представляют строку.

Автор: void* 25.7.2008, 23:26

Собственно продолжение. Простенький пример того, что можно сделать с помощью http://doc.trolltech.com/4.4-snapshot/stylesheet.html. Скомпилируйте это у себя и зацените кнопочку:

#include <QtGui>
int main(int argc, char **argv) {
    QApplication app(argc, argv);
    QPushButton btn;
    
    qApp->setStyleSheet(
          "QPushButton {"
             "border: 1px solid #000000;"
             "border-radius: 10px;"
             "background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
                                               "stop: 0 #dedede, stop: 0.5 #434343, stop: 0.51 #000000, stop: 1 #656a6d);"
             "min-width: 80px;"
         "}"

         "QPushButton:pressed {"
             "background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
                                               "stop: 0 #cfcccc, stop: 0.5 #333232, stop: 0.51 #000000, stop: 1 #585c5f);"
         "}"

         "QPushButton:flat {"
             "border: none; "
         "}"
         );
    btn.resize(100, 50);
    btn.show();
    return app.exec();
}

думаю вам понравится :)

Автор: Петров Виталий 18.9.2008, 8:31

Тоже поделюсь парой интересных моментов.

1) Если вы не используете потоки. но при этом имеется к примеру большой цикл, то интерфейс приложения может блокироваться и не перерисовывается.
Чтобы решить эту проблему можно вызывать метод QApplication::processEvents(); при каждой итерации цикла.

2) Сделать фон любого виджета прозрачным можно так:

listwidget->setFrameShape(QFrame::NoFrame);
listwidget->viewport()->setAutoFillBackground(false);

это может пригодится например, если вы пишете плазмоид для kde4 а в плазме пока нет некоторых нативных прозрачных виджетов =)))

Автор: void* 23.9.2008, 20:11

также можно регулировать прозрачность любого окошка с помощью метода QWidget::setWindowOpacity(), который принимает в качестве параметра вещественное число, где 1.0 - полная непрозрачность, а 0.0 - полная прозрачность. Соответственно по умолчанию это значение является равным 1.0

Автор: kwisp 26.11.2008, 17:58

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

1. если соединить один и тот же сигнал с одним и тем же слотом несколько раз(n), и вызвать сигнал один раз, то
слот вызовется столько раз сколько соединений с этим сигналом мы сделали(n):)....

2. если разъединить сигнал со слотом - то рушатся все соединения этого сигнала с этим слотом.

... известен ли кому нибудь метод узнать соединен ли сигнал со слотом и если соединен то сколько раз???

Автор: kwisp 29.11.2008, 12:13

Litkevich Yuriy, та ни ма за шо. ;)

Автор: Litkevich Yuriy 1.12.2008, 18:59

Цитата(kwisp @ 26.11.2008, 20:58) *
... известен ли кому нибудь метод узнать соединен ли сигнал со слотом и если соединен то сколько раз???
сообщения перенес в отдельную тему: http://www.forum.crossplatform.ru/index.php?showtopic=1720

Автор: SABROG 8.12.2008, 16:05

Использовать QLatin1String вместо QString где это возможно, т.к. первый быстрее.

QString - конвертирует копирует и конвертирует ASCII строку в Unicode каждый раз, когда ему передается строка в конструкторе.
QLatin1String - класс-обертка над Си строкой (char *). Строка никуда не копируется, поэтому программист должен сам следить за ней.

Имеет смысл использовать отдельно от визуальных компонентов, там где не нужна интернационализация.

Автор: molchanoviv 8.12.2008, 16:11

Только прирост этот будет незаметен. Так зачем этот геморой?

Автор: SABROG 8.12.2008, 16:17

Цитата(molchanoviv @ 8.12.2008, 16:11) *
Только прирост этот будет незаметен. Так зачем этот геморой?


Это смотря какая специфика приложения. Если предположить, что есть большой QList с такими строками и строки тоже не маленькие, а в приложении используется меньшая часть списка, то зачем конвертировать весь список сразу ? И экономия процессора и памяти, все-таки Unicode не однобайтовая кодировка.

Вопрос. Как редактировать свои посты, а то опечатки вылазят :) ?

Автор: SABROG 8.12.2008, 16:44

К теме о сигналах. Бывают случаи, когда надо испустить сигнал внутри обработчика событий, но подобное не работает, т.к. это может привести к зацикливанию и Qt блокирует такие сигналы. В метод connect можно передать дополнительный флаг Qt::QueuedConnection, что говорит Qt вызвать слот для сигнала при следующем цикле QEventLoop (т.е. после выхода из обработчика событий или метода). Такой прием обычно используется для помещения приложения в tray через кнопку minimize.

Автор: SABROG 8.12.2008, 18:01

В Qt есть возможность использовать системные иконки (разные для каждой ОС, определяются динамически). К сожалению в QtDesigner'e почему-то такой возможности нет и подобный метод рассчитан на ручную простановку иконок для каждого элемента. Поможет в этом метод QStyle::standardIcon(). Доступ к методу можно получить через указатель qApp->style()->standardIcon(...);

Выбор иконок не велик, но некоторые могут пригодится: http://doc.trolltech.com/main-snapshot/qstyle.html#StandardPixmap-enum

Автор: SABROG 9.12.2008, 14:31

В Qt можно соединять не только сигнал со слотом, но и сигнал с сигналом. Это приведет к тому, что сигнал запустит второй сигнал. При этом если первый и второй сигналы были подключены к слотам, то они тоже вызываются (естественно, если на нем что-то завязано, сигнал типа clicked() врятли кликнет на кнопку).

Автор: Admin 9.12.2008, 15:59

Треп перенесен в тему http://www.forum.crossplatform.ru/index.php?showtopic=1772.

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

Автор: Litkevich Yuriy 5.2.2009, 19:45

обнаружил интересное поведение в QTableView
если выделена ячейка в столбце N, то нажимая кнопки букв или цифр на клавиатуре будет осуществлятся навигация по этому столбцу.

например есть таблица:

id    RefDes    TypeName    Layer
52    C12    EMR-35V-47U(7*6.3*2.5) 47,0*35В    top
53    C17    EMR-35V-47U(7*6.3*2.5) 47,0*35В    top
54    DD2    ADM2483BRW     top
55    DD4    ADM2483BRW     top
56    FU1    MF-R017 MF-R017    top
57    FU2    MF-R017 MF-R017    top
58    FU3    MF-R017 MF-R017    top
59    R3    CR-0805 10к    top
60    R4    CR-0805 10к    top

если выделим ячейку с RefDes=C17, а затем будем нажимать кнопку "F", то выделение и фокус перейдет сначала на FU1, а при последующих нажатиях на FU2, FU3. Затем все повторится с начала.

Автор: SABROG 9.2.2009, 20:43

Не знаю куда это отнести к секретам или интересным возможностям. Вроде и не секрет, да и не возможность. А просто любопытная информация мимо которой многие могли бы пройти. Дерево классов Qt 4.3, распечатать и повесить на стенку. В ассистенте его нет, т.к. оно в PDF формате: http://doc.trolltech.com/extras/qt43-class-chart.pdf

Автор: Константин 11.2.2009, 6:15

ну и секреты у вас :)
но всё-равно внесу свои пять копеек:

если в кастомном виджете в paintEvent используется `setOpacity(a)` для активного Q(|Style)Painter, где `a<1.0` (например, эффект плавного показа/скрытия виджета), и у виджета имеется родитель, то соседи родителя получат такой же фактор прозрачности через /* кривую */ групповую прозрачность.
чтобы этого избежать, необходимо использовать сохранение/восстановление состояния QPainter:

void MyWidget::paintEvent(QPaintEvent*)
{
    QPainter painter(this);
    painter.save();
    painter.setOpacity(this->currentOpacityValue());

    //painter.drawSomething(...);
    painter.restore();
}

вышесказанное верно для Qt < 4.5, в 4.5 групповая прозрачность забыта, как страшный сон...

Автор: Admin 11.2.2009, 16:35

Перенес обсуждение не относящееся к данной теме в тему: http://www.forum.crossplatform.ru/index.php?showtopic=2138

Автор: SABROG 27.2.2009, 10:08

Еще один способ вывода отладочных сообщений для GUI приложений. Позволяет не прописывать CONFIG += console в .pro файле.
На qDebug, qWarning, QFatal будет всплывать диалог, вместо того, чтобы писаться в консоль.

MyClass::MyClass(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
#if 1 //0 - если надо выключить
    QErrorMessage *emsg = new QErrorMessage(this);
    emsg->qtHandler();
#endif
}



Автор: CrackedMind 13.3.2009, 12:59

Вообще этого будет более чем достаточно

Цитата
QErrorMessage::qtHandler(); // т.к. статический метод

Автор: BRE 13.3.2009, 13:09

Цитата(SABROG @ 27.2.2009, 10:08) *
Еще один способ вывода отладочных сообщений для GUI приложений. Позволяет не прописывать CONFIG += console в .pro файле.
На qDebug, qWarning, QFatal будет всплывать диалог, вместо того, чтобы писаться в консоль.

А еще можно перехватить весь вывод и писать его в лог-файлы, например.

QtMsgHandler qInstallMsgHandler ( QtMsgHandler handler )
В документации есть пример использования.

В проекте freeremote, я все строки добавляю в QStringListModel, что дает возможность в необходимых местах сделать просмотр лога, просто создав QListView и указав эту модель в качестве активной.

Автор: SABROG 13.3.2009, 14:49

Добавлю метод альтернативный методу QTimer::singleShot(), чтобы выполнить слот при возврате в главный event loop, т.е. после выхода из метода.

QMetaObject::invokeMethod(this, "nashSlot", Qt::QueuedConnection);


Этот код помещает в очередь событий информацию о том, что надо вызвать наш слот. Эквивалентно emit signalName с connection type Qt::QueuedConnection.

В принципе таким методом можно и сигналы симулировать.

Автор: SABROG 13.3.2009, 20:18

Пожалуй вопрос с enum'ом сюда помещу. В общем задача - получить название перечисляемого типа по значению. Решение вопроса в стиле Qt :)

#include <QtCore/QtGlobal>
#include <QtCore/QtDebug>
#include <QtCore/QMetaObject>
#include <QtCore/QMetaEnum>

class Programmer : public QObject
{
    Q_OBJECT
public:
    enum Language {ASM, C, CPP, PASCAL, FORTRAN, BASIC, JAVA};
    Q_ENUMS(Language)
};

int main(int argc, char *argv[])
{
    const QMetaObject &mo = Programmer::staticMetaObject;
    int index = mo.indexOfEnumerator("Language");
    QMetaEnum me = mo.enumerator(index);
    Programmer::Language p = Programmer::CPP;
    Programmer::Language l= static_cast<Programmer::Language>(me.keyToValue("CPP"));
    qDebug() << me.valueToKey(p) << ":" << l;
    return 0;
}

#include "main.moc"

Автор: Litkevich Yuriy 16.3.2009, 12:12

http://www.prog.org.ru/index.php?topic=8884.msg49877#msg49877

Автор: SABROG 20.3.2009, 15:02

Немножко заморочился на тему создания и извлечения zip архивов средствами Qt. Посмотреть что из этого вышло можно тут http://vingrad.ru/blogs/sabrog/2009/03/20/qt-45-rabota-s-zip-arhivami/

Автор: AD 20.3.2009, 15:12

SABROG, напиши, пожалуйста, тут о тех двух функциях-то! ;)

Автор: SABROG 20.3.2009, 15:15

Цитата(AD @ 20.3.2009, 15:12) *
SABROG, напиши, пожалуйста, тут о тех двух функциях-то! ;)

В Qt 4.5 если классы, которые используются для поддержки Open Document Format, среди них есть классы для работы с .zip архивами. В документации о них не слова и классы по сути только для внутреннего использования. Нас интересуют классы QZipReader и QZipWriter. Чтобы можно было их использовать надо:

- в .pro файле добавить это:
INCLUDEPATH += $(QTDIR)/src/gui/text

- в хедеры прописать пути:
#include <Qt/private/qzipreader_p.h>
#include <Qt/private/qzipwriter_p.h>
#include <qzip.cpp>


Использовать так:

//распаковка архивов
    QZipReader zip("file.zip"); // загружаем файл
    zip.extractAll("directory"); // извлекаем все что в нем в папку directory


С сжатием сложнее, но принцип такой:

    QZipWriter zip("file.zip"); // создаем файл
    zip.addDirectory("directory"); // добавляется пустая папка в zip архив с именем directory
    ....
//в созданную папку "directory" предыдущей командой мы добавляем файл file.ext.
//При этом мы сами должны открыть файл file.ext через QFile, прочитать все содержимое и передать как QByteArray в эту функцию
    zip.addFile("directory/file.ext", myByteArray);
    zip.close();

Автор: AD 20.3.2009, 16:40

:) Да нет! Я про аналоги getenv!!!!

Автор: SABROG 20.3.2009, 17:11

В Qt есть недокументированные кроссплатформменные функции qgetenv() и qputenv() для получения и установки переменных окружения.

QByteArray qgetenv(const char *varName);
bool qputenv(const char *varName, const QByteArray& value);


Описаны в <QtCore/QtGlobal>

Использовать на свой страх и риск.

Автор: Litkevich Yuriy 20.3.2009, 17:43

Цитата(SABROG @ 20.3.2009, 20:11) *
В Qt есть недокументированные кроссплатформменные функции qgetenv() и qputenv()
любопытно

Автор: SABROG 20.3.2009, 22:06

 qconfig_myconfig.zip ( 826 байт ) : 559


Для тех кто помешан на обезжиривании Qt. Вместе с исходниками Qt для мобильных систем поставляется утилита QConfig, которая позволяет отключать возможности (features).



При этом утилита сама смотрит за зависимостями и отключает зависимые компоненты.
Дистрибутивы для win и *nix идут без этой утилиты. Поэтому, чтобы воспользоваться данной утилитой нам понадобится скачать общий дистрибутив Qt для всех платформ. Сделать это можно в .torrent репозитории http://dist.trolltech.com/torrents/ Качать надо это: http://dist.trolltech.com/torrents/qt-all-opensource-src-4.5.0.zip.torrent

Сразу скажу, что у меня уже стояла Qt собранная статически, поэтому я просто зашел в папку $(QTDIR)/tools/qconfig и вбил заветные команды:

qmake
mingw32-make release


После этого я получил в папке release готовую утилиту. Я её залил сюда (3Mb): http://filebeam.com/b143c6b3d9bb05976e6ab1ea2fed7756

Далее запускаем её, не важно откуда. Появится диалог выбора файла. Надо выбрать файл $(QTDIR)/src/corelib/global/qfeatures.txt
Затем снимаем галки, которые нам не нужны и сохраняем в файл $(QTDIR)/src/corelib/global/qconfig-НАШЕИМЯ.h
Расположение и имя файла должно быть именно таким иначе он не найдется.

Затем используем не документированный для виндовых/линуксовых дистрибутивов, но существующий ключ -qconfig таким образом:
configure.exe -qconfig НАШЕИМЯ [тут идут остальные обычные параметры типа -confirm-license -static -no-exceptions и т.д.]


Дальше собираем как обычно. В аттаче я прикрепил свою конфигурацию, не уверен, что она оптимальная или правильная.

Проверил на примере /examples/widgets/calculator.
В обычном статике размер: 7,14 МБ (7 494 656 байт)
C теми же ключами но после редактирования через qconfig: 6,19 МБ (6 496 256 байт)
UPX сжал exe файл до : 2,34 МБ (2 463 232 байт)
Для сравнения обычная (shared) сборка, сумма всех файлов в папке: 13,1 МБ (13 779 036 байт)
---
Я пошел дальше в своих изысканиях и решил собрать Qt с оптимизацией по размеру. Отредактировал файл $QTDIR/mkspecs/win32-g++/qmake.conf и заменил строчку:
Цитата
QMAKE_CFLAGS_RELEASE = -O2

на
Цитата
QMAKE_CFLAGS_RELEASE = -Os

Проделал ту же операцию, что и раньше. Теперь пример calculator.exe сжатый UPX'ом весит: 1,99 МБ (2 095 104 байт)
Хоть и в ущерб скорости (оптимизация убрала выравнивание страниц), зато в пользу размера. Еще бы, есть разница 13Мб и 1,99Мб ;) ?

---
Немного погуглив нашел еще пару ключей для компилятора, которые прописал тоже в qmake.conf
Цитата
-fmerge-all-constants
-fno-default-inline
-fno-inline
-ffunction-sections
-fdata-sections
-Wl,--gc-sections

Новый рекорд: 1,86 МБ (1 951 744 байт)

Но есть побочный эффект. Простейшие примеры линкуются около минуты.


В общем экспериментируйте.

Автор: Litkevich Yuriy 20.3.2009, 22:21

любопытно

Автор: SABROG 22.3.2009, 16:28

Цитата(Litkevich Yuriy @ 20.3.2009, 22:21) *
любопытно

Обновил пост. Уже сам поражаюсь чего удалось добиться.

Автор: ViGOur 22.3.2009, 17:18

Тянет на статью в wiki. :)

Автор: Litkevich Yuriy 22.3.2009, 17:24

Цитата(ViGOur @ 22.3.2009, 20:18) *
Тянет на статью в wiki.
точно

Автор: CrackedMind 23.3.2009, 9:25

Лишь одно скажу. Если конфигурировать сразу так без предварительной компиляции

Цитата
configure -qconfig нашеимя

то утилиты не будут собираться, будет требовать dist-config

Если задефайнить QT_NO_SESSION_MANAGER то опять же сборка не пройдет. По крайней мере на каком-то снэпшоте 4.5 точно так было, починили ли они это в релизе без понятия. Да и зависимости там бывают странные.

Автор: Litkevich Yuriy 2.4.2009, 18:30

Эх, как бы всё это разгрести. Я уже несколько раз порывался. Но как только начну рисовать набумажке, что и куда, так волосы на голове начинают шевелиться.

Автор: Litkevich Yuriy 5.4.2009, 9:56

Ковырялся с документацией Qt 4.6 и сделал для себя открытие:
Оказвается в Qt 4.4 был введен новый класс компоновки http://doc.crossplatform.ru/qt/4.4.3/qformlayout.html
А его презентация появилась только в 4.5. на странице описания компоновок

Автор: CrackedMind 11.4.2009, 23:06

А кто-нибудь обратил внимание, что в 4.5 появились смарт-пойнтеры? Классы QSharedPointer & QWeakPointer.

Автор: Sokoloff 6.5.2009, 9:47

Обнаружил тут, что QImage умеет читать postscript файлы. Обнаружил случайно, смотрел пример из qt4/examples/widgets/imageviewer и вместо png файла кликнул на ps, и был нескозанно удивлен увидев что программа показала его.

На windows у меня не заработало (мо возможно нужно что-то доставить/настроить), но на kubunt-e 9.04 с QT 4.5.0 и ghostscript работает.

Автор: Litkevich Yuriy 6.5.2009, 10:06

Цитата(Sokoloff @ 6.5.2009, 13:47) *
ghostscript работает.
вот его в стандартной комплектации в виндовозе нет. Надо ставить какой-нибудь Ghostgum

Автор: Sokoloff 6.5.2009, 10:18

Цитата(Litkevich Yuriy @ 6.5.2009, 11:06) *
Ghostgum

Я ставил вот это http://pages.cs.wisc.edu/~ghost/doc/GPL/gpl864.htm но результата нет.
Впрочем, мне по windows и не надо.

Автор: SABROG 8.5.2009, 21:50

Думаю никто не будет против, если я буду использовать эту тему немного не по назначению? А именно как закладки, в каком-то смысле, чтобы не потерять.
_________

В документации по сигналам и слотам есть такая фраза:

Цитата
If several slots are connected to one signal, the slots will be executed one after the other, in an arbitrary order, when the signal is emitted.


Которая дает нам понять, что один сигнал подключенный к нескольким слотам не дает гарантии того, что эти слоты будут вызываться в порадке подключения сигналов. А именно в произвольном порядке.

Изучив немного посты в mailist'e trolltech я вынесл для себя следующую мысль. На данный момент порядок вызовов слотов можно предсказать, однако разработчики не дают гарантии того, что в новых версиях библиотеки порядок вызовов не изменится. Может быть вариант, когда вызовы будут сортироваться для достижения максимальной производительности. Поэтому, если нужен строгий порядок вызовов слотов по одному сигналу можно воспользоваться этим каркасом:

class X : public QObject
{
  Q_OBJECT
  ....  
signals:
       void fooPriority1();
       void fooPriority2();
       void fooPriority3();
       void fooPriority4();
slots:
       void emitFoo();
...
};
void X::emitFoo()
{
     emit fooPriority1();
     emit fooPriority2();
     emit fooPriority3();
     emit fooPriority4();
}

_________
А это уже на память мне, если захочу вспомнить как надо инициализировать seed перед использованием qrand:

qsrand(QDateTime::currentDateTime().toTime_t());

Автор: SABROG 20.6.2009, 15:02

Немного про нормализацию сигнатур. Мое внимание привлекло http://qt.gitorious.org/qt/pages/CodingConventions о необходимости нормализовывать все сигнатуры прежде чем использовать connect при изменении исходников Qt.

Нормализация представляет собой парсинг строки, уменьшение её длинны путем удаления лишних пробельных символов. Перестановка ключевых слов для оптимизации поиска нужного сигнала или слота у QObject'a. Подобный разбор происходит каждый раз, когда мы используем connect. Макросы SIGNAL() и SLOT() не дают нам нормализованную строку, они нам дают просто строку типа:

2mysignal(QString, QString)


После нормализации (QObject::normalizeSignature()) видно, что один пробел пропал:

2mysignal(QString,QString)


И т.п. В составе Qt появилась утилита /util/normalize, она пробегается по исходникам и заменяет макросы SIGNAL()/SLOT() на нормализованные сигнатуры, что сказывается на скорости соединения сигналов и слотов. Вероятно, в программах, где идет постоянное соединение/разъединение это помогло бы немного выйграть в скорости.

P.S.: однако мне не понятно, почему тролли не изменили moc компилятор таким образом, чтобы это делалось всегда на автомате.

Автор: SABROG 28.6.2009, 13:52

Пользователи Qt, которые собирают свои приложения с помощью компилятора gcc наверняка замечали, что при компиляции их приложений используется ключ оптимизации -O2. В первую очередь это связано с тем, что сама библиотека Qt собранная с ключем оптимизации -O3 ведет себя не стабильно. Однако никто вам не запрещает иметь библиотеку Qt собранную с оптимизацией -O2, а вашу программу с оптимизацией -O3 или любой другой. Однако ключи компилятора прописаны в одном из spec файлов. К тому же не удобно каждый раз лезть в исходники Qt и что-то там менять глобально для всех приложений. Я нашел несколько иной выход, это замена ключей компилятора на свои прямо в .pro файле:

#OPTIMIZE += 0 #no optimization
#OPTIMIZE += 1 #code size, execution time
#OPTIMIZE += 2 #code size, exectuion time, performance of generated code
#OPTIMIZE += 3 #as 2 and some more
#OPTIMIZE += s #only size code optimization
OPTIMIZE += 3

CONFIG(release, debug|release){
    !isEmpty(OPTIMIZE) {
        *-g++*{
        OPT += -O
        QMAKE_CFLAGS_RELEASE ~= s,-O.,$$join(OPT,,, $${OPTIMIZE}),
        QMAKE_CXXFLAGS_RELEASE ~= s,-O.,$$join(OPT,,, $${OPTIMIZE}),
        QMAKE_OBJECTIVE_CFLAGS_RELEASE ~= s,-O.,$$join(OPT,,, $${OPTIMIZE}),
        message(Selected optimization: $${QMAKE_CFLAGS_RELEASE})
        }
    }
}


Таким образом наша программа может быть собрана с максимальной оптимизацией, нужно лишь изменить значение переменной OPTIMIZE. К тому же это может быть полезным на этапе проектирования приложения. В больших проектах release сборка может собираться десятками минут. Конечно можно собирать только debug, но случаи бывают разными. Поэтому, чтобы уменьшить время компиляции релизных сборок можно отключить для них оптимизацию совсем (OPTIMIZE += 0). Когда, по мнению автора, программа будет готова к выпуску в свет - оптимизацию можно включить обратно. И конечно никто вам не запрещает на свой страх использовать тонкую настройку под разные платформы:
OPTIMIZE += 3 -mtune=core2 -march=core2 -mmmx -msse -msse2 -msse3 -msse4 -mfpmath=sse

Автор: Litkevich Yuriy 28.6.2009, 14:28

-O3 на самом деле не макисальная, а альтернативная оптимизация, и может существенно проигрывать -O2, это и в руководстве по GCC написано и по моей практике так.

Есть ещё -Os - оптимизация по размеру, жмёт основательно, я в контроллерах такую использую.

Автор: AD 28.6.2009, 15:04

0x - полная автоматизация. Ее ведь тоже можно использовать?

Автор: Litkevich Yuriy 28.6.2009, 15:27

Цитата(AD @ 28.6.2009, 19:04) *
0x
такого ключа в GCC нет.
Их всего вот: -O, -O0, -O1 -O2, -O3, -Os.

Цитата(AD @ 28.6.2009, 19:04) *
автоматизация
оптимизация :)

Автор: AD 28.6.2009, 16:19

Цитата(Litkevich Yuriy @ 28.6.2009, 16:27) *
оптимизация :)

Опечатка! :)

Я думал, ключи gcc и studio совпадают. Значит, ошибался....

Автор: trdm 28.6.2009, 16:45

Не такая уж и опечатка, :) у человека правильно мозги повернуты :)
автоматизация в некоторых случаях дает оптимизацю :)

Автор: AD 28.6.2009, 17:25

Цитата(trdm @ 28.6.2009, 17:45) *
Не такая уж и опечатка, :) у человека правильно мозги повернуты :)
автоматизация в некоторых случаях дает оптимизацю :)

:) Спасибо, конечно. Но в данном, конкретном случае эти ключики могут дать только оптимизацию! Сомневаюсь, что они могут повысить автоматизированность системы! :) Потому это действительно была опечатка!

Автор: SABROG 6.7.2009, 13:39

__fastcall в стиле Qt. В Delphi и MSVC есть аттрибут для функций __fastcall, который позволяет вызывать функции быстрее, т.к. при передаче параметров используются регистры eax, edx и ecx. К сожалению в связи с этим есть некоторые ограничения. Параметров может быть 3 и все они должны иметь целочисленный тип (4 байта). Например:

QString QT_FASTCALL MyClass::myFunc(int par1, ushort par2, uint *par3);

Чтобы использовать макрос QT_FASTCALL нужно подключить заголовок:

#include <QtCore/QtGlobal>


Само объявление макроса выглядит так:

#if defined(__i386__) || defined(_WIN32) || defined(_WIN32_WCE)
#  if defined(Q_CC_GNU)
#if !defined(Q_CC_INTEL) && ((100*(__GNUC__ - 0) + 10*(__GNUC_MINOR__ - 0) + __GNUC_PATCHLEVEL__) >= 332)
#    define QT_FASTCALL __attribute__((regparm(3)))
#else
#    define QT_FASTCALL
#endif
#  elif defined(Q_CC_MSVC) && (_MSC_VER > 1300 || defined(Q_CC_INTEL))
#    define QT_FASTCALL __fastcall
#  else
#     define QT_FASTCALL
#  endif
#else
#  define QT_FASTCALL
#endif

Автор: SergSh 27.8.2009, 9:49

QListView при инициализации считывает все строки для определения размеров.
На больших наборах данных это очень существенные затраты времени.

Чтобы этого не происходило достаточно воспользоваться setGridSize().

Правда у меня осталась другая проблема - предельное количество строк,
которое может отобразить QlistVIew - 134217724
Это огранечиние класса QVector. При этом объем, занимаемый приложением в памяти 520Mb.

Получил на форуме прикольный номер пользователя - килобайт 8)

Автор: SABROG 23.2.2010, 16:36

Кроссплатформенный метод установки приоритета для приложени. Навеяно этим http://lists.trolltech.com/qt-interest/2006-05/thread00831-0.html.

Вариант 1.

#include <QtCore/QCoreApplication>
#include <QtCore/QThread>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qApp->thread()->setPriority(QThread::HighestPriority);
    return a.exec();
}


Вариант 2. Установка приоритета после входа в цикл событий.
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <QtCore/QMetaType>

Q_DECLARE_METATYPE(QThread::Priority)
class CoreApplication : public QCoreApplication
{
        Q_OBJECT;
public:
        CoreApplication(int& argc, char* argv[], QThread::Priority priority = QThread::InheritPriority)
            : QCoreApplication(argc, argv)
        {
            qRegisterMetaType<QThread::Priority>();
            connect(this, SIGNAL(priorityChanged(QThread::Priority)),
                            SLOT(changePriority(QThread::Priority)),
                            Qt::QueuedConnection);
            if (priority != QThread::InheritPriority)
                setPriority(priority);
        }

        inline void setPriority(QThread::Priority priority) {emit priorityChanged(priority);}
        inline QThread::Priority priority() const {return thread()->priority();}
protected slots:
        void changePriority(QThread::Priority priority)
        {
            thread()->setPriority(priority);
        }
signals:
        void priorityChanged(QThread::Priority priority);
};

int main(int argc, char *argv[])
{
    CoreApplication a(argc, argv, QThread::HighestPriority);
    return a.exec();
}

#include "main.moc"

Вариант 3. Более легковесный вариант установки приоритета после входа в цикл событий, если вместо класса Object будет какой-нибудь MainWindow.
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>

class Object : public QObject
{
    Q_OBJECT;
public:
    Object(QObject *parent = 0) : QObject(parent) {}
    void setThreadPriority(QThread::Priority priority)
    {
        QMetaObject::invokeMethod(this, "changePriority", Qt::QueuedConnection, Q_ARG(int, static_cast<int>(priority)));
    }
protected:
    Q_INVOKABLE void changePriority(int priority)
    {
        qApp->thread()->setPriority(static_cast<QThread::Priority>(priority));
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
        Object object;
        object.setThreadPriority(QThread::HighestPriority);
    return a.exec();
}

#include "main.moc"


Для Windows стоит разделять понятия "класс приоритета приложения" и "приоритет потока". Все приложения изначально получают класс приоритета NORMAL_PRIORITY_CLASS и приоритет потока THREAD_PRIORITY_NORMAL. Предположим, что мы вызываем метод QThread::setPriority(QThread::TimeCriticalPriority), чтобы выделить самый высокий приоритет. К нашему удивлению в том же TaskManager'e приоритет будет стоять "Средний". Но в Windows количество возможных приоритетов аж 31 штука. Самый высокий достигается комбинацией установки класса REALTIME_PRIORITY_CLASS и приоритета THREAD_PRIORITY_TIME_CRITICAL. В Qt нет возможности установки класса приоритета, поэтому решением может быть вызов WINAPI ::SetPriorityClass(::GetCurrentProcess(), HIGH_PRIORITY_CLASS);, или установка класса через TaskManager, или установка класса приоритета в момент запуска приложения:

start /REALTIME myprogram.exe


Установка приоритета приложения для linux может не работать в некоторых ситуациях о чем сказано в документации к метод QThread::setPriority().

Автор: Litkevich Yuriy 23.2.2010, 19:22

Ну собственно один вариант ещё и в списке рассылке, Где создаётся пользовательское событие.

Автор: SABROG 23.2.2010, 19:39

Цитата(SergSh @ 27.8.2009, 9:49) *
Чтобы этого не происходило достаточно воспользоваться setGridSize().


Предпочтительней для этих целей использовать метод QListView::setUniformItemSizes(true), эффект аналогичный.

Автор: SABROG 2.4.2010, 0:05

Выложу сегодня 2 полезных "хака", которые могут пригодится при отладке приложения:

Первый хак - для доступа к приватным классам Qt:

namespace Hack { template <class To, class From> inline To* d_ptr(From* ptr){ return (To*)QObjectPrivate::get(ptr);}}


Эту функцию можно использовать например для включения функционала в QLineEdit, который пишет текст типа "введите сюда что-нибудь", когда в QLineEdit ничего нет:
#include "private/qlineedit_p.h"
...
{
    Hack::d_ptr<QLineEditPrivate>(ui->lineEdit)->placeholderText = "Test";
}


Или для установки своего виджета вместо QTableCornerButton в QTableView:

#include "private/qtableview_p.h"
...
QLabel* label = new QLabel(ui->tableWidget);
label->setText("<span style=\"color: red\">Bye</span> <span style=\"color: green\">Bye</span>, <span style=\"color: blue\">World!</span>");
label->setAlignment(Qt::AlignCenter);
QTableViewPrivate* tvPrivate = Hack::d_ptr<QTableViewPrivate>(ui->tableWidget);
tvPrivate->cornerWidget->deleteLater();
tvPrivate->cornerWidget = label;


Второй хак - для перевода перечеслений области имен Qt в текстовые строки:

struct StaticQtMetaObject : public QObject
{
    static inline const QMetaObject& get() {return staticQtMetaObject;}
};


Использовать так:

#include <QtCore/QtGlobal>
#include <QtCore/QtDebug>
#include <QtCore/QMetaEnum>

struct StaticQtMetaObject : public QObject
{
    static inline const QMetaObject& get() {return staticQtMetaObject;}
};

int main(int argc, char *argv[])
{
    const QMetaObject& mo = StaticQtMetaObject::get();
    int index = mo.indexOfEnumerator("Key");
    QMetaEnum me = mo.enumerator(index);
    Qt::Key myKey = Qt::Key_F35;
    qDebug() << me.valueToKey(myKey);
    return 0;
}


Методы опробованы на Qt версии 4.6.2, в других версиях они могут не работать.

Автор: loz 11.4.2010, 8:56

Думаю что в данной теме необходимо упомянуть об интересной возможности Qt - функции прямого копировани участков памяти :

void *qMemCopy(void* dest, const void* src, size_t n)

Данная функция копирует n байт из src в dest. Разумеется, вся ответственность за проверку размеров лежит на программисте.

Для примера использования могу показать приведение типов, обычными способами которое производится довольно трудоемко, а именно - мне надо было работать со строками в их битовом представлении. В Qt есть два класса - QString и QBitArray, возможности нормальной конвертации данных между ними я найти не смог, а требование к производительности кода было достаточно высоко, пришлось сделать так:
inline QString bitsToString(QBitArray& arr) {
        QString r;
        r.resize(arr.size() / 16);   // длина массива должна быть точно кратна размеру символа в UTF-16, 16 битам
        qMemCopy(r.data_ptr()->data, arr.data_ptr()->data+1, r.length()*2);   // т.к. 1 символ = 2 байта
        return(r);
};

inline QBitArray stringToBits(const QString& str) {
        QBitArray r(str.length()*16);    // 16 бит в символе
        qMemCopy(r.data_ptr()->data+1, str.constData(), str.length()*2);
        return (r);
}


И здесь еще есть инетересная особенность класса QBitArray, как вы заметили для обращения к данным в нем мы прибавляли к указателю 1, т.е. данны идут со второго байта, но почему?
А потому, что биты из QBitArray хранятся как реальные биты, но т.к. в памяти минимальная еденица хранения - байт, то что же делать с массивом например из 9 бит?
Так вот в этом первом байте как раз хранится количество используемых бит последнего байта массива. Все просто =)

Автор: Andrewshkovskii 17.4.2010, 15:48

Цитата(SABROG @ 2.4.2010, 1:05) *
Выложу сегодня 2 полезных "хака", которые могут пригодится при отладке приложения:

Первый хак - для доступа к приватным классам Qt:

namespace Hack { template <class To, class From> inline To* d_ptr(From* ptr){ return (To*)QObjectPrivate::get(ptr);}}


Эту функцию можно использовать например для включения функционала в QLineEdit, который пишет текст типа "введите сюда что-нибудь", когда в QLineEdit ничего нет:
#include "private/qlineedit_p.h"
...
{
    Hack::d_ptr<QLineEditPrivate>(ui->lineEdit)->placeholderText = "Test";
}

Вот у меня 2 вопроса :
что делает функция этого тимплейта?возвращает указатель на метод приватного класса по имени метода?(с тимплейтами знаком поверхностно, и с указателями на функции практически так же)
и 2ой :
Я так понимаю, метод и пропертя placeholderText для QLineEdit будет только в версии 4.7 ?

Автор: SABROG 19.4.2010, 12:13

Цитата(Andrewshkovskii @ 17.4.2010, 16:48) *
что делает функция этого тимплейта?

Возвращает указатель, который содержится в d_ptr переменной и приводит его к одному из приватному базовому классу. Например для QLineEdit можно получить сразу 3 приватных класса: QLineEditPrivate, QWidgetPrivate, QObjectPrivate. Нужный можно выбрать.

Цитата(Andrewshkovskii @ 17.4.2010, 16:48) *
Я так понимаю, метод и пропертя placeholderText для QLineEdit будет только в версии 4.7 ?

Да.

Автор: SABROG 24.4.2010, 11:55

Администратор одного программерского форума удалил мой блог, который я вел 2 года. Там был код, который я выкладывал в постах. Никакие кэши гугла и archive.org не сохранили информацию. В общем, если вы не против, то буду тут периодически выкладывать интересные/полезные решения.
---

Добавление сигнала clicked() в QLabel средствами State Machine Framework:

    QStateMachine* machine = new QStateMachine(this);
    QState* s1 = new QState(machine);
    QMouseEventTransition* mouseTrans =
            new QMouseEventTransition(ui->label, QEvent::MouseButtonRelease, Qt::LeftButton, s1);
    QObject::connect(mouseTrans, SIGNAL(triggered()), msgBox, SLOT(show()));
    machine->setInitialState(s1);
    machine->start();



Добавление флагов в свой класс на базе QObject'a и вывод состояния флагов в консоль:

// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include <QtCore/QObject>
class MyClass : public QObject
{
    Q_OBJECT
    Q_FLAGS(MyMode)

public:
    MyClass(QObject* parent = 0);

    enum MyModeFlag {
        NoOptions = 0,
        Option1 =   1L << 0,
        Option2 =   1L << 1,
        Option3 =   1L << 2,
        Option4 =   1L << 3,
        Option5 =   1L << 4
    };

    Q_DECLARE_FLAGS(MyMode, MyModeFlag)

    void setMode(MyMode m);
    inline MyMode mode() const;

private:
    MyMode m_mode;
};

Q_DECLARE_OPERATORS_FOR_FLAGS(MyClass::MyMode)

inline MyClass::MyMode MyClass::mode() const {return m_mode;}

class QDebug;
QDebug operator << (QDebug debug, MyClass::MyMode mode);

#endif // MYCLASS_H


// myclass.cpp

#include <QtCore/QDebug>
#include <QtCore/QMetaEnum>

#include "myclass.h"


MyClass::MyClass(QObject* parent)
        : QObject(parent), m_mode(MyClass::NoOptions)
{
}

void MyClass::setMode(MyMode m)
{
    m_mode = m;
}

QDebug operator << (QDebug debug, MyClass::MyMode modes)
{
    const QMetaObject& mo = MyClass::staticMetaObject;
    int index = mo.indexOfEnumerator("MyMode");
    QMetaEnum me = mo.enumerator(index);
    debug << me.valueToKeys(modes);
    return debug;
}


Результат:

"Option1|Option3|Option5"

Автор: Andrewshkovskii 24.4.2010, 12:11

Да, я тоже заметил, что удалили. Хотел инфу кое-какую почитать..и на тебе..:)А почему удалили, не сказали?

Автор: igor_bogomolov 24.4.2010, 12:11

Цитата( SABROG)
Администратор одного программерского форума удалил мой блог, который я вел 2 года
. Имеешь в виду свой блог на vingrade. Очень жаль. Заглядывал иногда. Они как нибудь обосновали свои действия?

З,Ы, Может и нам расширение для блогов поставить?

Автор: SABROG 24.4.2010, 15:44

Администратор это объяснил так:

Цитата
Все блоги, которые давно не обновлялись, и те, что использовались только для SEO были удалены.


Для SEO я блог не использовал. Обновлялся он конечно не часто, но это не повод удалять полезную информацию у которой нет срока давности.

Автор: SABROG 26.5.2010, 17:27

На заметку:

Заполняем Qt контейнер без for:

#include <algorithm>

#include <QtCore/QtGlobal>
#include <QtCore/QtDebug>
#include <QtCore/QList>

struct inc_functor
{
    inc_functor() : counter(0) {}
    inline int operator()() {return counter++;}
    int counter;
};

int main(int argc, char** argv)
{
    QList<int> testList;
    std::generate_n(std::back_insert_iterator<QList<int> >(testList),
                10, inc_functor());
    qDebug() << testList;
    return 0;
}


(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)


Ищем элемент контейнера удовлетворяющий нашим требованиям без for используя предикат:

// поиск первого неактивного (Disabled) виджета через аттрибуты
struct testAttributePredicate {
    testAttributePredicate(Qt::WidgetAttribute attrib) : m_attrib(attrib) {}
    inline bool operator()(QWidget* widget) {
        return widget->testAttribute(m_attrib);
    }
    Qt::WidgetAttribute m_attrib;
};
...
{
    QWidgetList widgets = qApp->allWidgets();
    QWidgetList::iterator result =
            std::find_if(widgets.begin(), widgets.end(),
                         testAttributePredicate(Qt::WA_Disabled));
    if (result != widgets.end())
        qDebug() << (*result)->objectName();
}


Передача указателя на метод класса для QtConcurrent::mapped() вместо обычной функции:

#include <QtConcurrentMap>
#include <functional>
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QVector<int> test;
    test << 1 << 2 << 3;

    QtConcurrent::mapped(test, std::bind1st(std::mem_fun(&MainWindow::myMethod), this));
}

int MainWindow::myMethod(int i)
{
    qDebug() << i;
    return i;
}

Автор: Litkevich Yuriy 26.5.2010, 19:17

а зачем в
struct testAttributePredicate
оператор operator()(QWidget* widget) ?

Автор: SABROG 26.5.2010, 20:13

Цитата(Litkevich Yuriy @ 26.5.2010, 20:17) *
а зачем в
struct testAttributePredicate
оператор operator()(QWidget* widget) ?


Так это обычный функтор, в функции find_if я создаю объект, передавая в конструктор параметр (аттрибут), который нужно искать, а стандартная библиотека в своем шаблоне осуществляет вызов уже через перегруженный оператор:

// приблизительное поведение (не исходный код шаблона find_if)
template<class InputIterator, class testAttributePredicate>
  InputIterator find_if ( InputIterator first, InputIterator last, testAttributePredicate obj)
  {
    for (; first!=last; first++ ) if ( obj(*first) ) break;
// obj(*first) - вызывается перегруженный оператор (). То есть функция заранее ожидает, что я его перегружу.
    return first;
  }

Автор: SABROG 21.6.2010, 18:23

Реализовал установку приоритета потока в параллельном программировании.

Как многие заметили в Qt есть метод QThread::setPriority(), а вот как задать приоритет потоку запущенному например через QtConcurrent::run() не понятно. В новом стандарте C++0x будет метод std::call_once, который является потокобезопасной функций для выполнения определеннной функции один лишь раз. Аналогичная функция есть и в BOOST'e. Посмотрев на код функции я пришел к выводу, что проще переписать её на Qt, чем заставлять людей тащить с собой BOOST. С другой стороны моя реализация получилась в 2 раза быстрей BOOST'овской, но медленней std::call_once. Связано это со скудными возможностями класса QAtomicInt, там где я бы мог обойтись быстрыми атомарными операциями загрузки (load) и сохранения (store), мне пришлось импровизировать и использовать более медленные атомарные методы (fetchAndStoreAcquire, fetchAndStoreRelease).

call_once.h

#ifndef CALL_ONCE_H
#define CALL_ONCE_H

#include <QtCore/QtGlobal>
#include <QtCore/QAtomicInt>
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtCore/QThreadStorage>
#include <QtCore/QThread>

namespace CallOnce {
    enum ECallOnce {
        CO_Request,
        CO_InProgress,
        CO_Finished
    };

    Q_GLOBAL_STATIC(QThreadStorage<QAtomicInt*>, once_flag)
}

template <class Function>
        inline static void qCallOnce(Function func, QBasicAtomicInt& flag)
{
    using namespace CallOnce;
    int protectFlag = flag.fetchAndStoreAcquire((int)flag);
    if (protectFlag == CO_Finished)
        return;
    if (protectFlag == CO_Request && flag.testAndSetRelaxed(protectFlag,
                                                            CO_InProgress)) {
        func();
        flag.fetchAndStoreRelease(CO_Finished);
    }
    else {
        do {
            QThread::yieldCurrentThread();
        }
        while (!flag.testAndSetAcquire(CO_Finished, CO_Finished));
    }
}

template <class Function>
        inline static void qCallOncePerThread(Function func)
{
    using namespace CallOnce;
    if (!once_flag()->hasLocalData()) {
        once_flag()->setLocalData(new QAtomicInt(CallOnce::CO_Request));
        qCallOnce(func, *once_flag()->localData());
    }
}

#endif // CALL_ONCE_H

call_once_test.cpp
#include <QtCore/QtGlobal>
#include <QtCore/QtDebug>

#include <QtCore/QTimer>

#include <QtCore/QTime>

#include <QtCore/QVector>
#include <QtCore/QThread>
#include <QtCore/QtConcurrentMap>
#include <QtCore/QtConcurrentFilter>
#include <QtCore/QCoreApplication>

#include <algorithm>

#include "call_once.h"

enum {Max = 100};

struct run_once
{
    void operator()()
    {
        qDebug() << "Functor: This only once...";
    }
};

void func_run_once()
{
    qDebug() << "Function: This only once...";
}

struct inc_functor
{
    inc_functor() : counter(0) {}
    inline int operator()() {return counter++;}
    int counter;
};

struct setPriorityFunctor
{
    setPriorityFunctor(QThread::Priority priority = QThread::NormalPriority)
        : m_priority(priority) {}
    inline void operator()()
    {
        QThread* thread = QThread::currentThread();
        thread->setPriority(m_priority);
    }
    QThread::Priority m_priority;
};

void setLowestPriorityFunction()
{
    QThread* thread = QThread::currentThread();
    thread->setPriority(QThread::LowestPriority);

    qDebug("Current thread %x. Thread set to Lowest priority",
           (quintptr)thread);
}

void setHighestPriorityFunction()
{
    QThread* thread = QThread::currentThread();
    thread->setPriority(QThread::HighestPriority);

    qDebug("Current thread %x. Thread set to Highest priority",
           (quintptr)thread);
}

int calculate(const int& num)
{
#if 0 // Test once call per thread with function
    #if 0 // Set lowest thread priority
        qCallOncePerThread(setLowestPriorityFunction);
    #else // Set highest thread priority
        qCallOncePerThread(setHighestPriorityFunction);
    #endif
#else // Test once call per thread with functor
    #if 0
        qCallOncePerThread(setPriorityFunctor(QThread::HighestPriority));
    #else
        qCallOncePerThread(setPriorityFunctor(QThread::LowestPriority));
    #endif
#endif

    return ~num;
}

static QBasicAtomicInt flag = Q_BASIC_ATOMIC_INITIALIZER(CallOnce::CO_Request);

int parityFilter(const int& num)
{
    qCallOnce(run_once(), flag);

    return num % 2 ? false : true;
}

static QBasicAtomicInt testflag = Q_BASIC_ATOMIC_INITIALIZER(CallOnce::CO_Request);

int main(int argc, char* argv[])
{
    QCoreApplication a(argc, argv);

    QTime t; t.start();
    for (int i = 0; i < 1000 * 1000 * 100; ++i)
        qCallOnce(run_once(), testflag);
    qDebug("%d ms", t.elapsed());

    QVector<int> ints;

    std::generate_n(std::back_insert_iterator<QVector<int> >(ints),
                                                (int)Max, inc_functor());

// Test qCallOnce
    {
        QVector<int> results = QtConcurrent::blockingMapped(ints, calculate);
        qDebug() << results;
    }

// Test qCallOncePerThread
    {
        QtConcurrent::blockingFilter(ints, parityFilter);
        qDebug() << ints;
    }

    return 0;
}


P.S.: функция также может пригодится для создания потоко-безопасных синглтонов.

 call_once.tar.gz ( 1.62 килобайт ) : 973
 

Автор: kuzulis 21.6.2010, 18:54

SABROG, а с Троллями (нокией) пробовали это обсудить? Мож они включат это в свой код?

Автор: SABROG 22.6.2010, 11:19

Цитата(kuzulis @ 21.6.2010, 19:54) *
SABROG, а с Троллями (нокией) пробовали это обсудить? Мож они включат это в свой код?

Если руки дойдут попробую сделать commit request для git'a.

Автор: SABROG 23.6.2010, 14:52

В старом проекте на Borland Builder C++ у меня были кнопочки со стилем csFramed. Захотелось узнать возможно ли такие сделать в Qt. Нашел флаг для свойства border в qss - groove, но как выяснислось в нем нельзя задать контрастные цвета например черный и белый. То есть задать можно один цвет на основе которого вычисляется второй. Цвета типа черный или белый не подходят, создается просто черный бордюр. Пришлось написать собственную версию:



qss

QPushButton {
/*top right bottom left (clockwise)*/
background-color: #d6d3ce;
border-width: 2px 3px 3px 2px;
border-style: solid;
border-color:
qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 white, stop:0.5 white, stop:0.51 #848284, stop:1 #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:1, stop:0 white, stop:0.33 white, stop:0.34 black, stop:0.67 black, stop:0.68 #848284, stop:1.0  #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 white, stop:0.33 white, stop:0.34 black, stop:0.67 black, stop:0.68 #848284, stop:1.0 #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:1, stop:0 white, stop:0.5 white, stop:0.51 #848284, stop:1 #848284);
}

QPushButton:default{
border-width: 3px 4px 4px 3px;
border-color:
qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 white, stop:0.33 white, stop:0.34 black, stop:0.67 black, stop:0.68 #848284, stop:1.0 #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:1, stop:0 white, stop:0.25 white, stop:0.26 black, stop:0.50 black, stop:0.51 black, stop:0.75 black, stop:0.76 #848284, stop:1 #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 white, stop:0.25 white, stop:0.26 black, stop:0.50 black, stop:0.51 black, stop:0.75 black, stop:0.76 #848284, stop:1 #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:1, stop:0 white, stop:0.33 white, stop:0.34 black, stop:0.67 black, stop:0.68 #848284, stop:1.0  #848284);
}

QPushButton:pressed{
border-width: 3px;
border-color:
qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 #848284, stop:0.33 #848284, stop:0.34 black, stop:0.67 black, stop:0.68 #848284, stop:1.0 #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:1, stop:0 white, stop:0.33 white, stop:0.34 black, stop:0.67 black, stop:0.68 #848284, stop:1.0  #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 white, stop:0.33 white, stop:0.34 black, stop:0.67 black, stop:0.68 #848284, stop:1.0 #848284)
qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:1, stop:0 #848284, stop:0.33 #848284, stop:0.34 black, stop:0.67 black, stop:0.68 #848284, stop:1.0  #848284);
}


Автор: SABROG 25.6.2010, 11:57

Продолжил издеваться над кнопками и qss:



qss

QPushButton {
font-weight: bold;
border: 1px solid black;
border-radius: 7px;
color: white;
background-color:
qradialgradient(spread:pad, cx:0.51, cy:-1.0, radius:2, fx:0.50, fy:0.0, stop:0.53 #81beff, stop:0.54 #268fff, stop:1 #268fff);
}

QPushButton:hover {
color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 black, stop:0.5 white);
}

QPushButton:pressed {
padding: 3px 0px 0px 3px;
background-color:
qradialgradient(spread:pad, cx:0.51, cy:-1.0, radius:2, fx:0.50, fy:0.0, stop:0.51 #0061ff, stop:0.52 #268fff, stop:1 #268fff);
}

QPushButton:default {
border-width: 2px;
}

QPushButton:focus {
border: 4px double black;
}


Автор: Алексей1153 1.8.2010, 11:53

Обнаружил любопытный спецэффект: если в качестве подменю (QMenu) задать элементу другое предыдущее по иерархии меню, то при выборе пункта все предыдущие подменю изчезают с экрана и показывается указанное ))

К примеру:

----------------
m1
----------------
A1 > (m2)
A2
A3
----------------



отделено от темы "http://www.forum.crossplatform.ru/index.php?showtopic=5334"

----------------
m2
----------------
A4
A5 > (m1)
A6
----------------

Выбор пунктов A1>A5 покажет на экране опять таки меню m1


---------------------------------------------------
Уточнил - не всё так радужно.


    #include <QMenu>

    QMap<QString,QAction*> map;
    const char* text=0;


    //менюшка первого уровня
    QMenu& m1=*new QMenu(this);
        text="11";        map[text]=m1.addAction(text);
        text="12";        map[text]=m1.addAction(text);
        text="13";        map[text]=m1.addAction(text);
        
    //менюшка второго уровня
    QMenu& m2=*new QMenu(this);
        text="21";        map[text]=m2.addAction(text);
        text="22";        map[text]=m2.addAction(text);
        text="23";        map[text]=m2.addAction(text);
        
    //менюшки третьего уровня
    QMenu& m3_1=*new QMenu(this);
        text="311";        map[text]=m3_1.addAction(text);
        text="312";        map[text]=m3_1.addAction(text);
        text="313";        map[text]=m3_1.addAction(text);
        text="314";        map[text]=m3_1.addAction(text);
    QMenu& m3_2=*new QMenu(this);
        text="321";        map[text]=m3_2.addAction(text);
        text="322";        map[text]=m3_2.addAction(text);
        text="323";        map[text]=m3_2.addAction(text);
    QMenu& m3_3=*new QMenu(this);
        text="331";        map[text]=m3_3.addAction(text);
        text="332";        map[text]=m3_3.addAction(text);
        text="333";        map[text]=m3_3.addAction(text);


    //связываем вложенные меню
    map["12"]->setMenu(&m2);

    map["21"]->setMenu(&m3_1);
    map["22"]->setMenu(&m3_2);
    map["23"]->setMenu(&m3_3);

    map["311"]->setMenu(&m1);
    map["311"]->setText("open m1");//откроется меню первого уровня
    
    map["312"]->setMenu(&m2);
    map["312"]->setText("open m2");//ничего не произойдёт
    
    map["313"]->setMenu(&m3_1);
    map["313"]->setText("open m3_1");//ничего не произойдёт, только подсветка прыгнет наверх
    
    map["314"]->setMenu(&m3_2);
    map["314"]->setText("open m3_2");//откроется ещё один уровень вложенности (стандартное поведение)

    //отображаем меню    
    m1.popup(cursor().pos());


1) если в в любом вложенном меню указать меню первого уровня в качестве вложенного далее, то выбор этого пункта закроет все подменю и покажет только меню первого уровня

2) если так же указать одно из промежуточных подменю, то ничего не произойдёт

3) если так же указать самого себя в качестве подменю, то просто переоткроется (визуально ничего не произойдёт, кроме того, что подсветка в текущеи открытом подменю прыгнет наверх)

4) если так же указать не учавствовавшее в цепочке подменю, оно просто откроется как вложенное

Автор: SABROG 20.8.2010, 18:25

Заметил интересную особенность при сериализации списка своих данных через оператор QDataStream в QSettings. Дело в том, что в принципе QDataStream умеет сериализовать QList, но для типа, который будет содержать контейнер нужно определять свои операторы <</>>. Предположим мы это сделали. Но при попытке сохранить список в QSettings мы получим ошибку, что тип не зарегестрирован как мета-тип:

error: ‘qt_metatype_id’ is not a member of ‘QMetaTypeId<QList<MyStruct> >’


Значит, чтобы поместить наш контейнер в QVariant надо еще прописать такую инструкцию:

Q_DECLARE_METATYPE(QList<MyStruct>)


Но и этого не достаточно. При запуске приложения мы получим ошибку:

QVariant::load: unable to load type 260.
QVariant::save: unable to save type 260.


Надо вызвать еще функцию:

qRegisterMetaTypeStreamOperators<QList<MyStruct> >("QList<MyStruct>");


Причем главное не напортачить с текстовым именем, оно должно точно совпадать с именем типа.

В итоге, чтобы поместить список с нашими данными, предположим, в .ini файл (QSettings) нужно прописать такое:

struct MyStruct
{
    QString name;
    qint32 key;
};

Q_DECLARE_METATYPE(MyStruct)
Q_DECLARE_METATYPE(QList<MyStruct>)

QDataStream& operator<<(QDataStream& ostream, const MyStruct& ms)
{
    ostream << ms.name << ms.key;
    return ostream;
}

QDataStream& operator>>(QDataStream& istream, MyStruct& ms)
{
    istream >> ms.name >> ms.key
    return istream;
}

int main(int argc, char* argv[])
{
    QCoreApplication app(argc, argv);

    QSettings qsettings("settings.ini", QSettings::IniFormat);
    qRegisterMetaTypeStreamOperators<MyStruct>("MyStruct");
    qRegisterMetaTypeStreamOperators<QList<MyStruct> >("QList<MyStruct>");

    QList<MyStruct> msList;
    MyStruct ms = {"MyStruct name", 123};
    msList << ms;
    qsettings.setValue("ListMyStruct", qVariantFromValue(msList));
    return 0;
}


Обратите внимание, там где вызывается второй раз qRegisterMetaTypeStreamOperators обычно предполагается, что вы где-то реализовали операторы
для сериализации этого типа, но у нас эти операторы реализованы только для MyStruct. Дело в том, что Qt сама реализовала эти операторы для класса QList, нам лишь нужно сообщить какого типа данные он будет содержать.

Автор: xls 31.8.2010, 11:31

Цитата
namespace Hack { template <class To, class From> inline To* d_ptr(From* ptr){ return (To*)QObjectPrivate::get(ptr);}}

К сожалению классы, у которых ##Private член не объявлен в *_p.h заголовочнике не позволяют сделать такой фокус.

Автор: SABROG 1.9.2010, 8:21

Цитата(Гость_xls_* @ 31.8.2010, 12:31) *
К сожалению классы, у которых ##Private член не объявлен в *_p.h заголовочнике не позволяют сделать такой фокус.

А конкретный пример можно?

Автор: xls 2.9.2010, 9:08

Цитата(SABROG @ 1.9.2010, 8:21) *
Цитата(Гость_xls_* @ 31.8.2010, 12:31) *
К сожалению классы, у которых ##Private член не объявлен в *_p.h заголовочнике не позволяют сделать такой фокус.

А конкретный пример можно?

QTabWidgetPrivate из QTabWidget

Автор: SABROG 5.9.2010, 18:55

Цитата(xls @ 2.9.2010, 10:08) *
QTabWidgetPrivate из QTabWidget

Через include можно подключить и .cpp файл, в надежде на то, что он удачно скомпилиться, что иногда бывает, умный оптимизатор выкинет код, который не используется.

Автор: xls 6.9.2010, 9:27

Цитата(SABROG @ 5.9.2010, 18:55) *
Через include можно подключить и .cpp файл, в надежде на то, что он удачно скомпилиться, что иногда бывает, умный оптимизатор выкинет код, который не используется.

Это будет не неиспользуемый, а дважды используемый код - один раз из моего cpp и второй из qt библиотеки - с соответствующим сообщением линковщика. ИМХО, лучше QObject::findChildren ничего еще не придумали.

Автор: SABROG 7.9.2010, 8:43

Цитата(xls @ 6.9.2010, 10:27) *
ИМХО, лучше QObject::findChildren ничего еще не придумали.

findChildren() не работает, если у объекта нет родителя, да и не все D-Pointer'ы - QObject'ы или имеют имена.

Я считаю, что Вы наткнулись скорее на исключение из правил. Думаю это не стандартная практика объявлять классы в .cpp файлах.

Автор: Гость_xls_* 13.11.2010, 12:35

Цитата(SABROG @ 7.9.2010, 8:43) *
Цитата(xls @ 6.9.2010, 10:27) *
ИМХО, лучше QObject::findChildren ничего еще не придумали.

findChildren() не работает, если у объекта нет родителя, да и не все D-Pointer'ы - QObject'ы или имеют имена.

Я считаю, что Вы наткнулись скорее на исключение из правил. Думаю это не стандартная практика объявлять классы в .cpp файлах.

А вот что пишут по этому поводу тролли:
//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists for the convenience
// of qsql*model.h .  This header file may change from version to version
// without notice, or even be removed.
//
// We mean it.
//

Автор: kwisp 13.11.2010, 13:15

Цитата(SABROG @ 7.9.2010, 8:43) *
Думаю это не стандартная практика объявлять классы в .cpp файлах.

почему же?
вполне нормально об этом Саттер и Элджер в частности пишут.
используется при создании фабрик обЪектов с использованием NVI - невиртуального интерфейса.

Автор: Yuriy 20.12.2010, 14:57

[quote name='Litkevich Yuriy' date='5.2.2009, 19:45' post='14041']
обнаружил интересное поведение в QTableView
если выделена ячейка в столбце N, то нажимая кнопки букв или цифр на клавиатуре будет осуществлятся навигация по этому столбцу


Здравствуйте! В QListWidget тоже присутствует такое поведение, при нажатии на букву, список перемещаеться на заглавную (нажатую) букву.
Вот, так может знает кто возможность отключения такого поведения , по причине , что при этом нету реакции на keyPressEvent(QKeyEvent *pe) . Реагирует только на кнопки Ф1 - Ф 12 , шифт, таб, и т.п

Автор: SABROG 18.1.2011, 20:11

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

    QFileSystemWatcher watcher;
    watcher.setObjectName(QLatin1String("_qt_autotest_force_engine_poller"));
    watcher.addPath(....);


На самом деле то, что идет до слова "poller", Qt особо не важно, главное, чтобы позиция слова в тексте совпадала.

Вы можете проголосовать, чтобы эту возможность включили в будущие дистрибутивы официально: http://bugreports.qt.nokia.com/browse/QTBUG-8351

Автор: igor_bogomolov 20.1.2011, 15:00

Если вы пишите плагины для QtDesigner и хотите что бы на форме виджет не просто отрисовывался, а вел себя как настоящий виджет, присвойте ему имя вида

comboBox->setObjectName("__qt__passive_xxx");
, где xxx - это любая строка. Для эксперимента, киньте на форму комбобокс, добавьте в него пару итемов, после чего задайте имя в формате описанном выше. Посмотрите как изменилось поведение виджета.

Автор: Алексей1153 22.1.2011, 22:05

Цитата(igor_bogomolov @ 20.1.2011, 17:00) *
Посмотрите как изменилось поведение виджета.

прикольно, но только после этого виджет не даёт редактировать свои свойства (помогает только откат действия)


Кстати, что я сюда зашёл то. Как раз тоже про комбобокс:
В поисках удобного выпадающего списка без прочих довесков, причём закрывающегося после потери фокуса или после выбора элемента, обнаружил следующее:

если создать комбобокс, но не показывать его (то есть он всегда hide() ), то, однако, список прекрасно вываливается методом showPopUp
Выбранный индекс берётся из сигнала currentIndexChanged.

перед выводом списка на экран, перед скрытием (hidePopup), очисткой или изменением элементов списка сигнал нужно отсоединять, чтобы не было ложных срабатываний выбора. Подключать же сигнал нужно сразу после метода showPopup


Появляется список под прямоугольником основного окна комбы, то есть так, как буд-то это окно было бы видимо. Если задать высоту комбы 0 (через setGeometry) , то верх списка будет совпадать с верхом комбы

Собственно, искомые мной свойства - закрывается по ESC или при потере фокуса

Автор: SABROG 25.1.2011, 22:15

В Wiki, на Developer Network нашел интересный вариант размещения окна по центру:

#include <QtGui/QStyle>
#include <QtGui/QDesktopWidget>
...
window->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, window->size(), qApp->desktop()->availableGeometry()));

Автор: aliks-os 6.3.2011, 21:24

Уважаемый Sabrog,
вы показали как записывать в ini - qsettings.setValue("ListMyStruct", qVariantFromValue(msList));. Был бы вам очень признателен, если бы вы в ваш пример добавили как считать записанные данные из ini файл, а то что то не особо получается...

Автор: Litkevich Yuriy 6.3.2011, 21:30

Цитата(aliks-os @ 6.3.2011, 23:24) *
а то что то не особо получается...
приведи код того как делаешь, но создав отдельную тему (что бы эту не засорять, она у нас как копилка)

Автор: Day 26.3.2012, 11:48

Цитата(Петров Виталий @ 18.9.2008, 8:31) *
Чтобы решить эту проблему можно вызывать метод QApplication::processEvents(); при каждой итерации цикла.
посмотрел этот метод у Макса Шлее. Нашел целых 2:
processEvents стр.602
processEvent стр.591
Видимо, последнее - опечатка? (в хедере не нашел)

Автор: Litkevich Yuriy 18.2.2013, 8:14

Обсуждение вынес в отдельную тему: http://www.forum.crossplatform.ru/index.php?showtopic=9232

Автор: PUMA 30.4.2015, 1:30

Цитата(SABROG @ 13.3.2009, 14:49) *
Добавлю метод альтернативный методу QTimer::singleShot(), чтобы выполнить слот при возврате в главный event loop, т.е. после выхода из метода.

QMetaObject::invokeMethod(this, "nashSlot", Qt::QueuedConnection);


Этот код помещает в очередь событий информацию о том, что надо вызвать наш слот. Эквивалентно emit signalName с connection type Qt::QueuedConnection.

В принципе таким методом можно и сигналы симулировать.


:) А есть способ убить слот, который стоит в очереди, но еще не выполнен? А посмотреть содержимое очереди?

Автор: Litkevich Yuriy 7.5.2015, 11:53

Цитата(PUMA @ 30.4.2015, 3:30) *
А есть способ убить слот, который стоит в очереди, но еще не выполнен? А посмотреть содержимое очереди?
простого вроде - нет.

Покопайся в файлах moc_***.cpp (которые генерит MOC), как я помню, там простой массив указателей, который обходится разом.

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)