Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Секреты и интересные возможности Qt
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Общие вопросы
Страницы: 1, 2
void*
Предлагаю в данную тему скидывать все известные вам интересные фичи и "секреты" 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) QComboBox-Изменение размера выпадающего списка
2) qmake, как обратится к текущему каталогу?
AD
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
void*, для 1-го пункта часто имя переменной коментируют, чтоб компиллер не ругался:
void someFunc(int /*var*/) {
     ...
}
void*
LitkevichВ Yuriy, приколупался :) лучше бы продолжил идею :)
Tonal
Цитата(AD @ 24.7.2008, 13:29) *
Не знаю, возможно, это и известный факт, но мне показалось не совсем обычным такое применение функции read/write. При этом применении она становится аналогом функций Windows Api ::ReadFile/::WriteFile:

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

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

Это обычно для программирования под *nix'ы read/write можно применить для любого дескриптора, в т.ч. для дескриптора файла, а файл в *nix'ах это всё что есть и может быть в системе. )))
Tonal
Цитата(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*
Собственно продолжение. Простенький пример того, что можно сделать с помощью Qt Style Sheets. Скомпилируйте это у себя и зацените кнопочку:
#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();
}

думаю вам понравится :)
Петров Виталий
Тоже поделюсь парой интересных моментов.

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

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

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

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

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

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

... известен ли кому нибудь метод узнать соединен ли сигнал со слотом и если соединен то сколько раз???
kwisp
Litkevich Yuriy, та ни ма за шо. ;)
Litkevich Yuriy
Цитата(kwisp @ 26.11.2008, 20:58) *
... известен ли кому нибудь метод узнать соединен ли сигнал со слотом и если соединен то сколько раз???
сообщения перенес в отдельную тему: Есть ли метод, чтобы узнать соединен ли сигнал со слотом?
SABROG
Использовать QLatin1String вместо QString где это возможно, т.к. первый быстрее.

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

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


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

Вопрос. Как редактировать свои посты, а то опечатки вылазят :) ?
SABROG
К теме о сигналах. Бывают случаи, когда надо испустить сигнал внутри обработчика событий, но подобное не работает, т.к. это может привести к зацикливанию и Qt блокирует такие сигналы. В метод connect можно передать дополнительный флаг Qt::QueuedConnection, что говорит Qt вызвать слот для сигнала при следующем цикле QEventLoop (т.е. после выхода из обработчика событий или метода). Такой прием обычно используется для помещения приложения в tray через кнопку minimize.
SABROG
В Qt есть возможность использовать системные иконки (разные для каждой ОС, определяются динамически). К сожалению в QtDesigner'e почему-то такой возможности нет и подобный метод рассчитан на ручную простановку иконок для каждого элемента. Поможет в этом метод QStyle::standardIcon(). Доступ к методу можно получить через указатель qApp->style()->standardIcon(...);

Выбор иконок не велик, но некоторые могут пригодится: http://doc.trolltech.com/main-snapshot/qst...dardPixmap-enum
SABROG
В Qt можно соединять не только сигнал со слотом, но и сигнал с сигналом. Это приведет к тому, что сигнал запустит второй сигнал. При этом если первый и второй сигналы были подключены к слотам, то они тоже вызываются (естественно, если на нем что-то завязано, сигнал типа clicked() врятли кликнет на кнопку).
Admin
Треп перенесен в тему Треп из темы Секреты и интересные возможности Qt.

Так как он всеже не изкореним в этой теме, то весь треп будет всегда переносится в ту тему...
Litkevich Yuriy
обнаружил интересное поведение в 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
Не знаю куда это отнести к секретам или интересным возможностям. Вроде и не секрет, да и не возможность. А просто любопытная информация мимо которой многие могли бы пройти. Дерево классов Qt 4.3, распечатать и повесить на стенку. В ассистенте его нет, т.к. оно в PDF формате: http://doc.trolltech.com/extras/qt43-class-chart.pdf
Константин
ну и секреты у вас :)
но всё-равно внесу свои пять копеек:

если в кастомном виджете в 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
Перенес обсуждение не относящееся к данной теме в тему: QAction::activated()[signal]
SABROG
Еще один способ вывода отладочных сообщений для 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
Вообще этого будет более чем достаточно
Цитата
QErrorMessage::qtHandler(); // т.к. статический метод
BRE
Цитата(SABROG @ 27.2.2009, 10:08) *
Еще один способ вывода отладочных сообщений для GUI приложений. Позволяет не прописывать CONFIG += console в .pro файле.
На qDebug, qWarning, QFatal будет всплывать диалог, вместо того, чтобы писаться в консоль.

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

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

В проекте freeremote, я все строки добавляю в QStringListModel, что дает возможность в необходимых местах сделать просмотр лога, просто создав QListView и указав эту модель в качестве активной.
SABROG
Добавлю метод альтернативный методу QTimer::singleShot(), чтобы выполнить слот при возврате в главный event loop, т.е. после выхода из метода.

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


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

В принципе таким методом можно и сигналы симулировать.
SABROG
Пожалуй вопрос с 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"
SABROG
Немножко заморочился на тему создания и извлечения zip архивов средствами Qt. Посмотреть что из этого вышло можно тут http://vingrad.ru/blogs/sabrog/2009/03/20/...s-zip-arhivami/
AD
SABROG, напиши, пожалуйста, тут о тех двух функциях-то! ;)
SABROG
Цитата(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
:) Да нет! Я про аналоги getenv!!!!
SABROG
В Qt есть недокументированные кроссплатформменные функции qgetenv() и qputenv() для получения и установки переменных окружения.

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


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

Использовать на свой страх и риск.
Litkevich Yuriy
Цитата(SABROG @ 20.3.2009, 20:11) *
В Qt есть недокументированные кроссплатформменные функции qgetenv() и qputenv()
любопытно
SABROG
Нажмите для просмотра прикрепленного файла

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



При этом утилита сама смотрит за зависимостями и отключает зависимые компоненты.
Дистрибутивы для win и *nix идут без этой утилиты. Поэтому, чтобы воспользоваться данной утилитой нам понадобится скачать общий дистрибутив Qt для всех платформ. Сделать это можно в .torrent репозитории http://dist.trolltech.com/torrents/ Качать надо это: http://dist.trolltech.com/torrents/qt-all-...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
любопытно
SABROG
Цитата(Litkevich Yuriy @ 20.3.2009, 22:21) *
любопытно

Обновил пост. Уже сам поражаюсь чего удалось добиться.
ViGOur
Тянет на статью в wiki. :)
Litkevich Yuriy
Цитата(ViGOur @ 22.3.2009, 20:18) *
Тянет на статью в wiki.
точно
CrackedMind
Лишь одно скажу. Если конфигурировать сразу так без предварительной компиляции
Цитата
configure -qconfig нашеимя

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

Если задефайнить QT_NO_SESSION_MANAGER то опять же сборка не пройдет. По крайней мере на каком-то снэпшоте 4.5 точно так было, починили ли они это в релизе без понятия. Да и зависимости там бывают странные.
Litkevich Yuriy
Эх, как бы всё это разгрести. Я уже несколько раз порывался. Но как только начну рисовать набумажке, что и куда, так волосы на голове начинают шевелиться.
Litkevich Yuriy
Ковырялся с документацией Qt 4.6 и сделал для себя открытие:
Оказвается в Qt 4.4 был введен новый класс компоновки QFormLayout
А его презентация появилась только в 4.5. на странице описания компоновок
CrackedMind
А кто-нибудь обратил внимание, что в 4.5 появились смарт-пойнтеры? Классы QSharedPointer & QWeakPointer.
Sokoloff
Обнаружил тут, что QImage умеет читать postscript файлы. Обнаружил случайно, смотрел пример из qt4/examples/widgets/imageviewer и вместо png файла кликнул на ps, и был нескозанно удивлен увидев что программа показала его.

На windows у меня не заработало (мо возможно нужно что-то доставить/настроить), но на kubunt-e 9.04 с QT 4.5.0 и ghostscript работает.
Litkevich Yuriy
Цитата(Sokoloff @ 6.5.2009, 13:47) *
ghostscript работает.
вот его в стандартной комплектации в виндовозе нет. Надо ставить какой-нибудь Ghostgum
Sokoloff
Цитата(Litkevich Yuriy @ 6.5.2009, 11:06) *
Ghostgum

Я ставил вот это http://pages.cs.wisc.edu/~ghost/doc/GPL/gpl864.htm но результата нет.
Впрочем, мне по windows и не надо.
SABROG
Думаю никто не будет против, если я буду использовать эту тему немного не по назначению? А именно как закладки, в каком-то смысле, чтобы не потерять.
_________

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

Цитата
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());
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.