Страницы

Поиск по вопросам

Показаны сообщения с ярлыком qt. Показать все сообщения
Показаны сообщения с ярлыком qt. Показать все сообщения

среда, 15 апреля 2020 г.

Дополнительное tty устройство

#qt #cpp #linux

                    
Есть задание надо: создать дополнительное tty устройство, что бы при запуске приложение
оно появлялось в системе и к нему подключалась другая программа.
Подскажите пожалуйста, как это реализовать программно.    


Ответы

Ответ 1



Спасибо всем, более правильный ответ на мой вопрос. -pseudo-terminal Как создать и работать с ним вот

Ответ 2



mknod, но аргументы надо где-то в описаниях системы искать. ls -l /dev говорит, что их много и major у всех 4. Похоже (по времени), что они создаются при загрузке ОС. Я, откровенно говоря, не понимаю с чем они на моей виртуалке связаны. Почитайте man 2 mknod, man 4 ttyS, man 4 tty и SEE ALSO в них. Может что-то полезное найдете.

понедельник, 13 апреля 2020 г.

Книги по Python + QT [дубликат]

#python #qt #книги

                            
             
                
                    
                        
                            На этот вопрос уже дан ответ здесь:
                            
                        
                    
                
                        
                            Книги и учебные ресурсы по Python
                                
                                    (1 ответ)
                                
                        
                Закрыт 4 года назад.
            
        

Что можно почитать русского о Python + QT? Желательно о PySide. И еще вопрос: есть
ли существенные отличия между PyQT и PySide?    


Ответы

Ответ 1



По PyQt есть книга Прохоренка. По PySide не встречал.

Игровой цикл

#qt

                    
Делаю кроссплатформенную игру с использованием Qt (из Линукса). Именно Qt выбрал,
потому что в перспективе понадобится графический интерфейс для всяких редакторов, а
также он предустановлен практически во все Linux'ы.
Пробовал использовать стандартный цикл QApplication::exec() и сделать таймер с нулевым
интервалом. Но всё это сильно дёргалось, хотя в полноэкранном режиме было ещё нормально.
Вот код:
QApplication app;
QTimer t;
t.setSingleShot(false);
t.start(0);
app.exec();

Потом попробовал сделать свой бесконечный цикл, вызывая processEvents, а после него
обновляя игру. Стало получше, но всё равно иногда дёргается в оконном режиме. Выдаёт
FPS от 50 до 70, хотя в Windows версии на WinAPI было больше 200. Код:
while(running)
{
    QApplication::processEvents();
    engine->Step();
}

Код, который я пробовал на WinAPI, когда ещё не перешёл на Linux:
while(GetMessage(...))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    engine->Step();
}

Вроде то же самое, что и на Qt, но на Qt тормозит, а на WinAPI нет. Хотя эксперимент
был не совсем чистым, так FPS на WinAPI я замерял на Windows, а FPS на Qt я замерял
в Linux.    


Ответы

Ответ 1



Обычно пишут для каждой платформы пачку платформозависимых файлов, которые обрабатывают ввод, собирают информацию о системе, инициализируют отдельные потоки для звука и приводят это все к форме принятой в движке. Затем где-нибудь в одном из файлов для каждой системы вставляют: int main(int argc, char *argv[]) { core.init(); while(1) //Специальная олимпиада открыта вдруг for(;;) быстрее { core.drawFrame(); } return 0; //should never get here } При этом сам кросс-платформенный код отрисовки инкапсулирован в core. Сам этот метод может быть достаточно сложен. Там могут быть счетчики FPS, работа с вводом , обработка AI, столкновений, логики и лишь в конце рендеринг/очистка памяти. При этом рендеринг осуществляется разными техниками для разных объектов/материалов в несколько проходов с разными шейдерами в разные буферы. По поводу редакторов для игры, очень сомнительно что ему реально нужна кросс-платформенность и непонятно что он будет делать. В лучшем случае комбинировать меши выгруженные из нормального 3D редактора в уровень. Но если игра маленькая это можно сделать и в любом другом 3D редакторе. А файл с описанием уровня/активов просто автоматически сгенерировать скриптом. По поводу core.init() это тоже зависит от реализации. Если игра маленькая она просто загружает все что можно загрузить. Если большая то грузится основной GUI, шейдеры, метафайл с описанием остальных активов, пользовательский кэш, загрузочный экран/заставка и.т.д. Геометрия и текстуры уровня же подгружаются отдельно по сигналу от GUI из обработчика ввода внутри drawFrame если пользователь решил таки загрузить очередной уровнеь. По поводу скачков FPS то непонятно каким образом вы его считаете. Взвешенное(weighted) скользящее среднее по 20ке последних тиков вроде неплохой метод. http://en.wikipedia.org/wiki/Moving_average P.S.Код Doom 3, на который я ориентируюсь сделан таким образом, если смотреть на то как он справляется с покрытием нескольких платформ.

Ответ 2



Оказалось, что дело не в главном цикле Qt. Попробовал перекомпилировать под Windows, где у меня уже был реализован WinAPI-вариант, там тот же самый низкий FPS. Сделал через XLib на Linux'е - вроде чуть плавнее, но FPS вроде не увеличился. Единственное что изменилось при портировании и не было проверено - это компилятор MSVC сменился на g++. Я пробовал также llvm-g++ и clang++, но на производительности никак не отражается. Да и дебаг от релиза по производительности ничем почти не отличается, а в студии сильно отличалось. Вообще не могу понять, что же такое произошло во время портирования. Были подозрения на некоторые изменения, но я попробовал их повторить в старой версии движка в студии и они никак не повлияли на высокий FPS. Так что буду учиться пользоваться Linux'овским профайлером. Ещё попробую создать отладочный контекст OpenGL и задействовать GL_AMD_debug_output. Может он что-нибудь скажет. Если нет, то попробую скомпилировать проект в студии.

Qt5: подключение библиотек

#qt

                    
Пытаюсь написать простую программу, подключаю
 #include 

но пишет, что библиотека не найдена. 
Раньше было так же с 
#include , но поменял на #include 
Искал в интернете нашел, что надо добавить 
 QT += core gui
 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

Не помогает, что делать?    


Ответы

Ответ 1



Я сталкивался с такой проблемой. Дело в том что в Qt 5 все виджеты переехали из модуля QtGui в новый модуль QtWidgets. По этому в проектном файле (заголовочный .pro) необходимо добавить модуль виджетов - примерно так: QT += widgets в место старого модуля (хотя его можно оставить ) QT += gui А в исходных файлах заменить модуль QtGui на модуль QtWidgets. Заменить строку : #include на строку : #include В вашем случае будет достаточно изменения только в проектного файла, но что бы развернуть ответ полностью я добавил пару строк. Либо просто скопируйте и вставьте : QT += core gui widgets greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

Ответ 2



#include

суббота, 11 апреля 2020 г.

Как подключить отладчик GDB для Qt Creator 2.5.2 под виндовс?

#отладка #gdb #qtcreator #qt #cpp

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


    


Ответы

Ответ 1



Нашел. См. скриншот:

Сессии, авторизация

#cpp #qt #сессия #qt5 #auth

                    
Пишу небольшое клиент-серверное приложение на Qt5, встал вопрос об авторизации.
Подскажите актуальные алгоритмы для безопасной авторизации и генерации ID сессии,
был бы рад полистать готовые реализации и примеры.
    


Ответы

Ответ 1



Частично на этот вопрос отвечает " RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication ".Возможно, доступное по этой ссылке. В целом, передавайте хешированный ключ с солью (т.е. по каналу связи следует передавать только хеш-сумму). На стороне сервера хешируйте ключ пользователя с той же солью и сравнивайте хеши. Такой вид аутентификации используется в линукс при авторизации, например. Смотрите также "дайджест-аутентификация". Upd: @Timofey Bondarev, спасибо за ценное замечание.

Ответ 2



Категорически не рекомендую использовать всякие srand() + rand() для генерации чувствительных данных. На винде вполне можно использовать CryptGenRandom из CryptoAPI. В линухах, говорят, /dev/urandom.

Как должны правильно взаимодействовать модель и QSortFilterProxyModel?

#cpp #qt #qt5 #qml

                    
Приветствую! 
Ситуация следующая:
Есть моделька, основанная на QAbstractItemModel, так как данных достаточно много,
получаю их с помощью предназначенного для этого canFetchMore и fetchMore
и когда просто модель отображается в ListView (qml), всё замечательно, данные подгружаются
по мере пролистывания.

Теперь на эту модельку сверху вешаю прокси, на основе QSortFilterProxyModel
в ней для теста отбираю строки (переопределив filterAcceptsRow) по какому нибудь
полю, не важно. 

Дальше начинаются проблемы. 
При установленной фильтрации во вьюшке выводятся не все возможные значения!
т.е. выводятся только загруженные в fetchMore в данный момент. 
Т.е. в fetchMore гружу по 100 записей, не выводятся ни одной, т.к. целевые значения
будут прогружены начиная с 500.
и прогрузки дальше не происходит т.к. логично, что вьюшка не считает необходимым
загружать ещё т.к. всё умещается и не надо пролистывать но и прокси моделька не считает
нужным прогрузить дальше, либо как-то сообщить основной модели - мне нужны записи с
вот такими значениями.

Подскажите, пожалуйста, как быть? 
Не, я согласен, что лучше всего будет выбирать нужные значение через запрос к базе,
что бы не грузить лишнего,
но как тогда лучше сделать связь с основной моделью? 

т.е. например я в fetchRow основной модели прогрузил записи от 0 до 100, в прокси
модели нужно выбирать, которые располагаются начиная с 500, ок, я говорю основной модели
- прогрузи ка записи с вот такими значенияи, если есть.
Ок, гружу. 

Но возникает следующая проблема - если запись например 555 добавлена в модель, то
при скролинге вьюшки, она попросит записи с 500 по 600 и я тогда снова добавлю запись
555. т.е. получается будет дублирование. 
Запоминать записи, которые грузил? что-то костылём попахивает. 
Может есть красивый выход из ситуации?

Спасибо, даже за подсказки, на что смотреть!
    


Ответы

Ответ 1



На мой взгляд, тут может быть только три варианта для решения проблемы. Первый - самый бестолковый, если данных очень много. Предполагает, что модель-источник должна загрузить сразу все данные и не использовать fetchMore в принципе. Второе решение может быть основано на переопределении метода fetchMore() у прокси с тем условием, что будет сразу же (рекурсивно) повторять запрос на чтение новых строк из исходной модели до тех пор, пока количество строк в прокси после filterAcceptsRow() не достигнет нужного числа, либо исходная модель не прочитает все данные. Фактически, это решение почти столь же бестолковое, что и первое, так как потребуется полное считывание всех данных исходной модели при определённых условиях фильтрации. Третье решение предполагает использование фильтрации данных непосредственно в модели-источнике. Обратите внимание, что все SQL-модели из состава Qt имеют возможность фильтровать данные самостоятельно и в общем случае не нуждаются в каких-либо прокси. Если модель-источник является таблицей в БД, то имеет смысл использовать классы, специально предназначенные для работы с подобными источниками, а не заниматься наследованием абстрактной модели. Если же модель-источник не является таблицей в БД, а, например, представляет из себя некий файл, то фильтрация данных должна быть добавлена в эту исходную модель и производиться непосредственно при вызове fetchMore() с тем, чтобы выдать минимальное количество данных, соответствующих условиям фильтрации.

Не собирается Qt. Ошибка 0x2

#cpp #qt #visual_cpp

                    
Хотел пересобрать Qt5.4.2 под Windows через компилятор VC++ (cl.exe) компании Microsoft.

но на финише (или далеко от финиша) мне выбились ошибки:


  LINK : fatal error LNK1181: cannot open input file 'qsqlite.lib'
  
  'cd' return code 0x2


Как исправить? С WinGW, конечно, всё собирается. Но мне нужны 64-разрядные приложения
(MinGW64 не поддерживается официально самой Qt), поэтому хотелось бы собрать через
компилятор VC.
    


Ответы

Ответ 1



Есть два варианта: qsqlite.lib у вас собрана или нет. Если нет - смотреть на логи сборки и понимать почему нет, но это уже несколько иная проблема. Если она собрана - смотреть флаги линкера в плане дополнительных путей для либ.

Ответ 2



Надеюсь вы собираете в чистой директории? А не в той же директории, в которой вы собирали помощью Mingw? Скорее всего эта библиотека (qsqlite.lib) нужна не для сборки самой qt, а для сборки примеров или тестов. Попробуйте так: configure -nomake examples -nomake tests -static Я вообще так собираю статически: configure -debug-and-release -opengl desktop -no-angle -skip qtwebkit -no-icu -release -nomake tests -nomake examples -static -platform win32-msvc2013 -no-sse2 jom module-qtbase (потому что мне ничего кроме qtbase не нужно)

четверг, 9 апреля 2020 г.

QByteArray во float

#qt #float

                    
Есть QByteArray, в котором лежат несколько float. Нужно их оттуда вынуть и записать
в соответствующие переменные. Пока единственным решением нашел копирование области
памяти с помощью memcpy:

float f1, f2;
memcpy(&f1, byteArray.data(), sizeof(float));
memcpy(&f2, byteArray.data()+sizeof(float), sizeof(float));


Если пытаться использовать QDataStream, то ничего путного не выходит, т.к. он пытается
считать не 4 байта, а 8. Какие решения еще можно найти?
    


Ответы

Ответ 1



Используйте класс QDataStream и setFloatingPointPrecision(). QByteArray arr; float f1, f2; QDataStream ds(&arr, QIODevice::ReadOnly); ds.setFloatingPointPrecision(QDataStream::SinglePrecision); ds >> f1 >> f2; Чтобы попеременно читать double и float, нужно переключать режим точности. QByteArray arr; float f1; double f2; QDataStream ds(&arr, QIODevice::ReadOnly); ds.setFloatingPointPrecision(QDataStream::SinglePrecision); ds >> f1; ds.setFloatingPointPrecision(QDataStream::DoublePrecision); ds >> f2; Либо можно читать "сырые" байты: QDataStream ds(&arr, QIODevice::ReadOnly); ds.readRawData(reinterpret_cast(&f1), sizeof(f1)); ds.readRawData(reinterpret_cast(&f2), sizeof(f2));

вторник, 7 апреля 2020 г.

как правильно организовать MVC модель?

#qt #mvc

                    
Пишу учебное задание, пытаюсь написать некоторое подобие MVC. Пытаюсь разделить логику
программы и интерфейса в разные классы. Соответственно, запилил классы Controller и
View. View содержит нажатия всяких кнопок, считывание с полей, обновление содержимого
меток и т.п. Controller же должен по идее инициировать запуск View и считывать получаемые
значения для проведения операций над ними и передачи этого добра в модель. Я сделал
вьюху элементом контроллера, вызываю её, но столкнулся с проблемой - а как, собственно
получать из вьюхи данные? Т.е. например, я понимаю, что можно запустить view->show()
и прочие методы вызывать из контроллера, но как обеспечить свободную работу с формой
пользователю и при этом вовремя получать информацию о его действиях?
    


Ответы

Ответ 1



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

четверг, 2 апреля 2020 г.

Парсинг XSD С++

#cpp #qt #xml #xsd

                    
Привет всем. Возникла задача, на основе XML и XSD динамически подгружать интерфейс.
Написал xml и xsd файл к этой xml. Загружаю xsd файл в память, проверяю по xsd схеме
xml файл,с помощью библиотеки libxml2, выводит все хорошо или есть где нибудь ошибка,
но к примеру допустим все хорошо. Следующим этапом провожу парсинг xml файла средствами
Qt (QXmlReader), создаю необходимые формы. Теперь необходимо, как то достать из xsd
схемы значения ограничения диапазона, типы и другие атрибуты. К примеру в xsd схеме
хранится диапазон для формы, пример маленького кусочка большого файла привел ниже:


 
  
     
     
  
 



Вот мне нужно из определённого элемента в XSD достать: 
1) тип integer
2) minInclusive(мин значение диапазона) - 0
3) maxInclusive(максимальное значение диапазона) - 120

На основе этих значение для формы к примеру spinBox будут заданы ограничения 1) если
получили тип integer в результате парсинга xsd, будет создан объект класса QIntValidate
(для делегата) и будет установлен setRange(0,120); для этого spinBox

Возможно есть ли какие нибудь библиотеки для парсинга самой XSD, а не только XML?
Может кто то покажет пример xsd в libxml2, но я не нашел. Возможно кто то знает хорошие
примеры, как это можно сделать или делал это когда-нибудь средствами Qt. 
    


Ответы

Ответ 1



Схема XSD - это тоже файл в формате XML. Вы знаете его структуру. Можете тем-же парсером его парсить. Например, ищете узел с тэгом "xs:element" и со свойством "name" равному "age". В нём - узел с тэгом "xs:simpleType". В нём узел с тегом "xs:restriction". В нем в узлах с тегами "xs:minInclusive" и "xs:maxInclusive" берёте из свойства "value" необходимые вам значения ограничений.

Ответ 2



Посмотрите "CodeSynthesis XSD", может подойдёт. Есть более мощный продукт Altova XSD Editor (коммерческий), тоже c++ умеет генерить

вторник, 31 марта 2020 г.

Как получить информацию о подключенном устройстве?

#linux #windows #qt #winapi #usb

Я пишу на С++, использую Qt под Windows. Мне надо получить: название модели, изготовителя,
серийный номер устройства(видео камеры, фото , квадрокоптера , флешки или SD Card)
и путь до устройства. Желательно кроссплатформенное решение.


Есть ответ SetupDiGetDeviceProperty. Но тут не кроссплатформенное решение , к тому
же я не совсем понимаю как получить путь до устройства.
Читал о библиотеки libusb. Но тут не выводятся название модели, изготовителя, серийный
номер устройства. То что выводиться :

Dev (bus 2, device 0): 8086 - 1E31
Dev (bus 1, device 4): 04A9 - 1795
Dev (bus 1, device 3): A4Tech - USB Mouse
Dev (bus 3, device 0): 8086 - 1E26
Dev (bus 1, device 1): 8087 - 0024
Dev (bus 1, device 0): 8086 - 1E2D
Dev (bus 1, device 2): 0BDA - 0151
Dev (bus 1, device 6): Logitech Inc. - Logitech USB Headset H340
Dev (bus 3, device 2): 8087 - 0024
Dev (bus 3, device 3): Logitech - USB Keyboard
Dev (bus 3, device 1): 04A9 - 3206


Хотя была вставлена флешка и подключена камера.
Есть изящное решение этой задачи. Если кто сталкивался с такой задачей поделитесь
кодом пожалуйста.
    


Ответы

Ответ 1



К сожалению, используя библиотеку libusb под Windows нельзя вытянуть всю эту информацию без установки специального драйвера WinUSB. Можно только получить VID и PID (шестнадцатеричные значения типа 8086 - 1E31 в вашем примере), и по ним найти производителя и устройство в таблице известных идентификаторов, например здесь. Производителя и серийный номер можно получить, используя Setup API и IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION. Букву диска тоже можно получить только средствами Setup API, это несколько сложнее. Для этого нужно вначале построить таблицу номеров дисков для всех используемых букв дисков с помощью IOCTL_STORAGE_GET_DEVICE_NUMBER; затем открыть интересующее устройство (запоминающее устройство, которое является дочерним для USB-устройства), найти его номер тем же IOCTL, сравнить его со значениями из таблицы и таким образом определить букву. Как-то так: #include #include #include #include #include #include #include #undef LowSpeed #include #include #pragma comment(lib,"Setupapi.lib") void ErrorMes(LPCTSTR lpszFunction) { // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); // Display the error message lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen(lpszFunction) + 40) * sizeof(TCHAR)); wprintf(L"%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); } //Поиск номера диска для USB-устройства BOOL FindDiskNumber(LPCWSTR match, STORAGE_DEVICE_NUMBER* lpret){ BOOL retval = FALSE; DEVPROPTYPE dpt=0; wchar_t buffer[1024]=L""; TCHAR id_upper[1024]=L""; DWORD RequiredSize = 0; SP_DEVINFO_DATA devinfo = { 0 }; SP_DEVICE_INTERFACE_DATA deviceInterface = { 0 }; PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetail = NULL; BOOL res; HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (deviceInfoHandle != INVALID_HANDLE_VALUE) { int deviceIndex = 0; while (true) { ZeroMemory(&deviceInterface,sizeof(deviceInterface)); deviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); //получение всех дисков if (SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &GUID_DEVINTERFACE_DISK, deviceIndex, &deviceInterface)) { DWORD cbRequired = 0; SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, 0, 0, &cbRequired, 0); if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { deviceInterfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[cbRequired]); memset(deviceInterfaceDetail, 0, cbRequired); deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if (!SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, deviceInterfaceDetail, cbRequired, &cbRequired, 0)) { goto Next; } // Initialize the structure before using it. memset(deviceInterfaceDetail, 0, cbRequired); deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); // Call the API a second time to retrieve the actual // device path string. BOOL status = SetupDiGetDeviceInterfaceDetail( deviceInfoHandle, // Handle to device information set &deviceInterface, // Pointer to current node in devinfo set deviceInterfaceDetail, // Pointer to buffer to receive device path cbRequired, // Length of user-allocated buffer &cbRequired, // Pointer to arg to receive required buffer length NULL); // Not interested in additional data //получение информации о устройстве ZeroMemory(&devinfo,sizeof(devinfo)); devinfo.cbSize = sizeof(SP_DEVINFO_DATA); BOOL success = SetupDiEnumDeviceInfo(deviceInfoHandle, deviceIndex, &devinfo); if(success==FALSE){ErrorMes(L"SetupDiEnumDeviceInfo"); goto Next;} res=SetupDiGetDeviceProperty(deviceInfoHandle,&devinfo, &DEVPKEY_Device_Parent,&dpt,(PBYTE)buffer,1000,NULL,0); if(res==FALSE){ErrorMes(L"SetupDiGetDeviceProperty");goto Next;} int len = wcslen(buffer); for(int i=0;iDevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if(handle==INVALID_HANDLE_VALUE) { ErrorMes(L"CreateFile");goto Next; } STORAGE_DEVICE_NUMBER sdn = {0}; DWORD nbytes = 0; //получение номера диска res = DeviceIoControl(handle, // handle to device IOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCode NULL, // lpInBuffer 0, // nInBufferSize (LPVOID) &sdn, // output buffer (DWORD) sizeof(sdn), // size of output buffer (LPDWORD) &nbytes, // number of bytes returned NULL // OVERLAPPED structure ); CloseHandle(handle); if(res != FALSE){ //устройство найдено memcpy(lpret,&sdn,sizeof(sdn)); retval = TRUE; } else ErrorMes(L"DeviceIoControl"); } } } else { break; } Next: if(deviceInterfaceDetail != NULL){ delete[] deviceInterfaceDetail; deviceInterfaceDetail = NULL; } if(retval != FALSE) break; //устройство уже найдено deviceIndex++; //следующее устройство } SetupDiDestroyDeviceInfoList(deviceInfoHandle); } else ErrorMes(L"SetupDiGetClassDevs"); return retval; } //таблица номеров для всех дисков bool disk_IsUsed[30]={0}; STORAGE_DEVICE_NUMBER disk_number[30]={0}; int _tmain(int argc, _TCHAR* argv[]) { setlocale(LC_ALL,"Russian"); HANDLE hVol; DWORD nbytes; WCHAR letter; WCHAR volume[100]={0}; int i; STORAGE_DEVICE_NUMBER sdn={0}; //заполняем таблицу номеров для дисков for(i=0;i<30;i++){ letter = 'A' + i; wsprintf(volume,L"\\\\.\\%c:",letter); disk_IsUsed[i] = FALSE; hVol = CreateFile( volume, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if(hVol == NULL || hVol == INVALID_HANDLE_VALUE){continue;} BOOL ret = DeviceIoControl(hVol, // handle to device IOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCode NULL, // lpInBuffer 0, // nInBufferSize (LPVOID) &(disk_number[i]), // output buffer (DWORD) sizeof(STORAGE_DEVICE_NUMBER), // size of output buffer (LPDWORD) &nbytes, // number of bytes returned NULL // OVERLAPPED structure ); CloseHandle(hVol); if(ret == FALSE){continue;} disk_IsUsed[i] = TRUE; } WCHAR match[1024]={0}; GUID guid = { 0xF18A0E88, 0xC30C, 0x11D0, { 0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8 } }; /*USB HUB Interface class GUID*/ HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (deviceInfoHandle != INVALID_HANDLE_VALUE) { int deviceIndex = 0; while (true) { SP_DEVICE_INTERFACE_DATA deviceInterface = { 0 }; deviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); //получение всех USB-концентраторов if (SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, deviceIndex, &deviceInterface)) { DWORD cbRequired = 0; SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, 0, 0, &cbRequired, 0); if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[cbRequired]); memset(deviceInterfaceDetail, 0, cbRequired); deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if (!SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, deviceInterfaceDetail, cbRequired, &cbRequired, 0)) { delete[] deviceInterfaceDetail; deviceIndex++; continue; } // Initialize the structure before using it. memset(deviceInterfaceDetail, 0, cbRequired); deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); // Call the API a second time to retrieve the actual // device path string. BOOL status = SetupDiGetDeviceInterfaceDetail( deviceInfoHandle, // Handle to device information set &deviceInterface, // Pointer to current node in devinfo set deviceInterfaceDetail, // Pointer to buffer to receive device path cbRequired, // Length of user-allocated buffer &cbRequired, // Pointer to arg to receive required buffer length NULL); // Not interested in additional data BOOL res; /*Открываем устройство для отправки IOCTL*/ HANDLE handle = CreateFile(deviceInterfaceDetail->DevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if(handle!=INVALID_HANDLE_VALUE) { DWORD bytes_read=0; UINT ports = 0; USB_NODE_INFORMATION nodeinfo; USB_NODE_CONNECTION_INFORMATION_EX coninfo={0}; const UINT BUFSIZE = 1000; char buffer[BUFSIZE]={0}; USB_DESCRIPTOR_REQUEST* req = (USB_DESCRIPTOR_REQUEST*)&buffer; USB_STRING_DESCRIPTOR* desc; ZeroMemory(&nodeinfo,sizeof(nodeinfo)); nodeinfo.NodeType = UsbHub; //получаем число портов на концентраторе res=DeviceIoControl(handle,IOCTL_USB_GET_NODE_INFORMATION , &nodeinfo,sizeof(nodeinfo),&nodeinfo,sizeof(nodeinfo),&bytes_read,0); if(res==FALSE)ErrorMes(L"DeviceIoControl"); else ports = (UINT)nodeinfo.u.HubInformation.HubDescriptor.bNumberOfPorts; for(int j=1;j<=ports;j++){ ZeroMemory(&coninfo,sizeof(coninfo)); coninfo.ConnectionIndex=j; //получаем инфу о порте res=DeviceIoControl(handle,IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX , &coninfo,sizeof(coninfo),&coninfo,sizeof(coninfo),&bytes_read,0); if(res==FALSE){ErrorMes(L"DeviceIoControl");continue;} if(coninfo.ConnectionStatus==0) continue; //нет устройства printf("\n- Hub %2d, Port %2d: USB v%x device\n",deviceIndex, j,(int)coninfo.DeviceDescriptor.bcdUSB); printf("VID: %04X PID: %04X\n",(int)coninfo.DeviceDescriptor.idVendor ,(int)coninfo.DeviceDescriptor.idProduct); //формируем строку для поиска устройства wsprintf(match,L"VID_%04X&PID_%04X",(UINT)coninfo.DeviceDescriptor.idVendor ,(UINT)coninfo.DeviceDescriptor.idProduct); /*Serial number*/ ZeroMemory(buffer,BUFSIZE); req->ConnectionIndex=j; req->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) + coninfo.DeviceDescriptor.iSerialNumber; req->SetupPacket.wLength = BUFSIZE - sizeof(USB_DESCRIPTOR_REQUEST); req->SetupPacket.wIndex = 0x409; //US English res=DeviceIoControl(handle,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION , &buffer,sizeof(buffer),&buffer,sizeof(buffer),&bytes_read,0); if(res==FALSE){ErrorMes(L"DeviceIoControl");continue;} desc = (USB_STRING_DESCRIPTOR*)(&req->Data[0]); wprintf(L"Serial: %s\n",desc->bString); /*Vendor*/ ZeroMemory(buffer,BUFSIZE); req->ConnectionIndex=j; req->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) + coninfo.DeviceDescriptor.iManufacturer; req->SetupPacket.wLength = BUFSIZE - sizeof(USB_DESCRIPTOR_REQUEST); req->SetupPacket.wIndex = 0x409; //US English res=DeviceIoControl(handle,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION , &buffer,sizeof(buffer),&buffer,sizeof(buffer),&bytes_read,0); if(res==FALSE){ErrorMes(L"DeviceIoControl");continue;} desc = (USB_STRING_DESCRIPTOR*)(&req->Data[0]); wprintf(L"Vendor: %s\n",desc->bString); /*Product*/ ZeroMemory(buffer,BUFSIZE); req->ConnectionIndex=j; req->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) + coninfo.DeviceDescriptor.iProduct; req->SetupPacket.wLength = BUFSIZE - sizeof(USB_DESCRIPTOR_REQUEST); req->SetupPacket.wIndex = 0x409; //US English res=DeviceIoControl(handle,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION , &buffer,sizeof(buffer),&buffer,sizeof(buffer),&bytes_read,0); if(res==FALSE){ErrorMes(L"DeviceIoControl");continue;} desc = (USB_STRING_DESCRIPTOR*)(&req->Data[0]); wprintf(L"Product: %s\n",desc->bString); /*Disk letter*/ res = FindDiskNumber(match,&sdn); if(res != FALSE) { for(i=0;i<30;i++) if( disk_IsUsed[i] !=FALSE && disk_number[i].DeviceNumber == sdn.DeviceNumber && disk_number[i].DeviceType == sdn.DeviceType) { letter = 'A' + i; printf("Disk letter: %c\n",letter); } } else printf("Disk letter not found\n"); } CloseHandle(handle); }else{ ErrorMes(L"CreateFile");//failed to open device }//endif delete[] deviceInterfaceDetail; } } else { break; } ++deviceIndex; } SetupDiDestroyDeviceInfoList(deviceInfoHandle); } else ErrorMes(L"SetupDiGetClassDevs"); system("PAUSE"); return 0; } Должно вывести подобный результат: ‐ Hub 3, Port 1: USB v200 device VID: 8564 PID: 1000 Serial: 5B94314A Vendor: JetFlash Product: Mass Storage Device Disk letter: G ‐ Hub 3, Port 4: USB v110 device VID: 09DA PID: 000A Serial: ? Vendor: A4Tech Product: USB Mouse Disk letter not found ‐ Hub 3, Port 6: USB v110 device VID: 046E PID: 5505 Serial: ? Vendor: BTC Product: USB Multimedia Keyboard Disk letter not found ‐ Hub 3, Port 10: USB v200 device VID: 04E8 PID: 689E Serial: 1234567890ABCDEF Vendor: SAMSUNG Electronics Co., Ltd. Product: Samsung Android USB Device Disk letter: H

Имитация нажатии клавиши

#cpp #qt #winapi #visual_cpp #qt5

Имитацию нажатии клавиши делаю так: 

keybd_event('C', 0,0,0);
keybd_event('C', 0,KEYEVENTF_KEYUP,0);


Как сделать что бы имитация нажатии клавиши было только в определенном окне
    


Ответы

Ответ 1



Нет Windows ОС под рукой, чтобы проверить, но все же, попробуйте. HWND wndHndl = FindWindow(_T("ApplicationName"), NULL); PostMessage(wndHndl, WM_KEYDOWN, VK_A, 0);

понедельник, 30 марта 2020 г.

QLayout не изменяет размеры виджета

#cpp #qt

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

Необходимо, чтобы это работало так:

main.cpp

#include 
#include "MainWindow.h"

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


    QApplication app(argc, argv);
    MainWindow window;
    window.show();

    return app.exec();
}


MainWindow.h  

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

#include "CentralWidget.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT
    private:
        CentralWidget *m_centralWidget;

    public:
        explicit MainWindow(QMainWindow *parent=nullptr);
};

#endif   


MainWindow.cpp  

#include "MainWindow.h"

MainWindow::MainWindow(QMainWindow* parent ) : QMainWindow(parent)
{
    m_centralWidget = new CentralWidget(this);
    setCentralWidget(m_centralWidget);
}  


CentralWidget.h  

#ifndef CENTRALWIDGET_H
#define CENTRALWIDGET_H

#include 
#include 
#include 
#include "qcustomplot.h"

class CentralWidget : public QWidget
{
    Q_OBJECT
    private:
        QCustomPlot *m_plotWidget;
        QTableWidget *m_tableWidget;

        virtual void resizeEvent(QResizeEvent *event);
    public:
        explicit CentralWidget(QWidget *parent=nullptr);
};

#endif   


CentralWidget.cpp  

#include 
#include 

#include "CentralWidget.h"

CentralWidget::CentralWidget(QWidget* parent) : QWidget(parent)
{
    m_plotWidget = new QCustomPlot;
    m_tableWidget = new QTableWidget;


    QHBoxLayout *layout = new QHBoxLayout;

    layout->addWidget(m_plotWidget);
    layout->addWidget(m_tableWidget);



    setLayout(layout);
}

void CentralWidget::resizeEvent(QResizeEvent *event)
{
    //Костыль
    //m_plotWidget->setGeometry(15, 15, width() / 2 - 15, height() - 30);
    //m_tableWidget->setGeometry(width() / 2 + 15, 15, width() / 2 - 30, height() - 30);

    qDebug() << m_plotWidget->size();
    qDebug() << m_tableWidget->size();
}

    


Ответы

Ответ 1



m_plotWidget->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); Подробности по QSizePolicy смотри в документации.

Принудительный вызов ToolTip

#cpp #qt

Есть QLineEdit с установленным tooltip, в которое записываются численные значения.
Хочется вызывать tooltip у этого поля при некорректном вводе. 

Как реализовать подобное, не через connect: смотреть корректность ввода —> вызывать
искусственно какой-нибудь метод с mouseEvent?

UPD:

    


Ответы

Ответ 1



Самое простое, это использование QToolTip::showText: QToolTip::showText(line_edit->mapToGlobal(QPoint()), line_edit->toolTip()); mapToGlobal(QPoint()) -- возвращает глобальные координаты виджета, от левого верхнего угла. Другой вариант -- послать событие QHelpEvent нужному виджету, используя QCoreApplication::postEvent QEvent* event = new QHelpEvent(QEvent::ToolTip, line_edit->pos(), line_edit->mapToGlobal(QPoint())); QCoreApplication::postEvent(line_edit, event);

воскресенье, 29 марта 2020 г.

QT C++ работа с базой данных в разных потоках

#cpp #qt

Как можно модифицировать ПО для мультипоточной работы. 

Хотелось бы увидеть минимальный базовый пример, дабы разобраться и пока не лезть в дебри.

Есть большая таблица(100 тыс наименований) при их запросе виснет GUI.
Хотелось бы это устранить.

Логика была такая.


Человек открывает программу.
Конструктор формы отправил запрос потоку в БД.
Строится каркас приложения.
Берём данные из потока в основной.
Заполняем полученными данными таблицу.


p.s
Пробовал сделать подобное на сигналах и слотах с применением QThread. Отправлял запрос
в другой поток, но он начинал работать только когда закроется основное приложение...



mainwindow.СPP

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    //QSqlDatabase objDatabase;
    objDatabase = QSqlDatabase::addDatabase("QMYSQL");
    objDatabase.setDatabaseName("librarydb");
    objDatabase.setHostName("127.0.0.1");
    objDatabase.setPort(3306);
    objDatabase.setUserName("hays0503");
    objDatabase.setPassword("hays0503");
    objDatabase.open();

    //QSqlTableModel *objTableModel;
    objTableModel = new QSqlTableModel();
    objTableModel->setTable("author");
    if (!objTableModel->select())
    {
        qDebug()<<"Error";
    }else {
       ui->tableView->setModel(objTableModel);
    }
}


mainwindow.H

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 

#include 
#include 
#include 
#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QSqlDatabase objDatabase;
    QSqlTableModel *objTableModel;
};

#endif // MAINWINDOW_H

    


Ответы

Ответ 1



void MainWindow::on_pushButton_clicked() { QFuture future = QtConcurrent::run( [this]() { // Исполняем этот код в другом потоке //QSqlDatabase objDatabase; objDatabase = QSqlDatabase::addDatabase("QMYSQL"); objDatabase.setDatabaseName("librarydb"); objDatabase.setHostName("127.0.0.1"); objDatabase.setPort(3306); objDatabase.setUserName("hays0503"); objDatabase.setPassword("hays0503"); objDatabase.open(); //QSqlTableModel *objTableModel; objTableModel = new QSqlTableModel(this); objTableModel->setTable("author"); if (!objTableModel->select()) { delete objTableModel; objTableModel = nullptr; qDebug()<<"Error"; } return objTableModel; }); QFutureWatcher *watcher = new QFutureWatcher(this); connect(watcher, SIGNAL(finished()), watcher, [this, watcher]() { // Запускаем этот код в UI потоке, когда объект future завершит свою работу в рабочем потоке ui->tableView->setModel(watcher->result()); watcher->deleteLater(); // Удалим ненужный watcher }, Qt::QueuedConnection); ); watcher->setFuture(future); // Связываем watcher с feature. Это быстрая операция и не тормозит поток UI } Если много раз кликать на кнопку, то будет запущено сразу много одинаковых задач. Надо не забывать, что работать с объектами objDatabase и objTableModel можно только из одного потока одновременно.

Нужно ли добавлять .ui файлы в список исходников cmake?

#cpp #qt #qt5 #cmake #qt_designer

Нужно ли добавлять .ui файлы в список исходников cmake?

То есть писать 

add_executable(my_target main.cpp main.h main.ui)


или 

add_executable(my_target main.cpp main.h)


?
    


Ответы

Ответ 1



Как уже написали, в add_executable указывать *.ui-файлы не обязательно. При включённом set(CMAKE_AUTOUIC ON) cmake сам просмотрит все *.cpp файлы на предмет наличия в них include'ов вида ui_*.h и сам обработает соответствующие *.ui файлы. При сборке без AUTOUIC (по каким-либо причинам), .ui файл необходимо передать макросу qt5_wrap_ui(), который явно создаст команду для сборки ui_*.h, а хедер уже сам подтянется по зависимостям к исходнику, который его включает. С другой стороны это не ошибка, add_executable (и прочие) допускают указание в списке исходников файлы, которые непосредственно не участвуют в сборке; в данном случае они не добавляются в качестве зависимости к конкретной цели. Обычно мотивацией к этому является использование IDE. В этом случае проект для оной, сгенерированный cmake, будет включать их в качестве «дополнительных исходников», и они будут отображаться в списке файлов связанных с целью наряду с остальными. Также, человек, поддерживающий проект, может захотеть указать их просто «для себя», чтобы было явно видно при сборке какого файла используется конкретный ui.

Ответ 2



ui файлы не нужно перечислять в add_executable или add_library - это лишено смысла. Эти файлы обрабатываются с помощью uic, так что это он должен знать о том, где искать эти файлы (именно искать). Если ui файлы находяться в тоже директории, то все норм, а если в другой, то нужно установть опцию: set(CMAKE_AUTOUIC_SEARCH_PATHS "paht/to/uic/files")

Ответ 3



На сегодняшний день я не слышал о прямой поддержке .ui файлов в add_executable(). В этой команде перечисляются лишь исходники, которые CMake умеет "собирать" - .c,.cpp,.h,.o,.asm. Файлы .ui обрабатываются специальным макросом qt5_wrap_ui() из модуля Qt5Widgets. См. документацию.

Права администратора в Qt

#cpp #qt #root

Есть приложение на Qt, и обычно располагается в Program Files. В некоторых случаях,
когда запускается программа, она запрашивает/не запрашивает права администратора. 
Что нужно дописать, чтобы запускался всегда с правами админа.
    


Ответы

Ответ 1



Чтобы программа запрашивала права администратора в Windows, нужно внедрить в неё файл манифеста. Файл внедряется в готовый исполняемый файл программой mt. Обычно она устанавливается вместе с Visual Studio. Если вы не используете Visual Studio, то вам придётся установить эту программу отдельно, читайте соответствующую документацию. Файл манифеста может выглядеть следующим образом: Description of application Сохраните этот файл под именем manifest.xml рядом с вашим исполняемым файлом. После этого запустите в командной строке: mt -manifest manifest.xml -outputresource:your_program.exe Чтобы манифест автоматически внедрялся в файл при сборке проекта в Qt Creator, нужно добавить в pro-файл сценарий пост-линковки: win32 { QMAKE_POST_LINK += mt -manifest $$PWD/manifest.xml -outputresource:$$OUT_PWD/$$TARGET".exe" $$escape_expand(\n\t) }

Фильтр для каждого столбца в QTableView

#qt #qwidget

Имеется tableview, который отображает некоторую модель данных. В модели несколько
столбцов (текст, числа, чекбоксы).

Так же есть прокси модель, которая фильтрует данные.

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

В идеале, хочется засунуть виджеты прямо в шапку tableview. Но для этого нужно написать
свой QHeaderView и нагородить кучу кода, что не есть хорошо.

Думаю над вариантом: накидать на форму несколько lineedit в какой-то layout, чтоб
это автоматически подстраивалось под qtableview по размерам и в итоге красиво отображалось.
Чтобы каждый фильтр-виджет был именно над тем столбцом, для которого он предназначен.

Какие есть инструменты для решения такой задачи?

    


Ответы

Ответ 1



Думаю в шапку виджет засунуть не получится, если верить докам: Note: Each header renders the data for each section itself, and does not rely on a delegate. As a result, calling a header's setItemDelegate() function will have no effect. Как вариант - при клацанье на шапку нужной секции над ней появляется виджет с lineedit'ом, и при вводе текста в него вызывать член-функции сортировки. Вот пример кода картинки выше: QStandardItemModel* model = new QStandardItemModel; for (int row = 0; row < 4; ++row) { for (int column = 0; column < 4; ++column) { QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column)); model->setItem(row, column, item); } } ui->tableView->setModel(model); //При клацанье на заголовок секции открывать вижет сортировки connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked, [this](int section) { QHeaderView* headerView = ui->tableView->horizontalHeader(); QWidget* sortWidget = new QWidget(this, Qt::Popup); sortWidget->setAttribute(Qt::WA_TranslucentBackground); sortWidget->setStyleSheet("background-color: rgba(255, 255, 255, 128);" "color: white;"); QHBoxLayout* lo = new QHBoxLayout(sortWidget); QLineEdit* le = new QLineEdit(sortWidget); connect(le, &QLineEdit::textChanged, [this](const QString& toSort) { //TODO вызываем логику сортировки }); lo->addWidget(le); sortWidget->setLayout(lo); sortWidget->resize(headerView->sectionSize(section), 30); sortWidget->show(); const auto sx = section * headerView->sectionSize(section) + (this->pos().x() + ui->tableView->pos().x()); const auto sy = this->pos().y() + ui->tableView->pos().y(); sortWidget->move(sx, sy); }); Только лучше конечно всю эту красоту инкапсулировать в какой-нибудь SortPopUpWidget.

Ответ 2



Спасибо @magrif, Ваше решение вдохновило меня. Я решил задачу таким способом. Идею реализации я описал выше в вопросе. Однако виджеты создаются программно (динамически). Заранее, руками, на форму я добавил layout, в котором буду хранить виджеты. Это сверху, над tableview. Подключаю модель к tableview ui->view_scan->setModel(m_scanning_filter_model); ui->view_scan->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Stretch); Теперь мы знаем кол-во столбцов. Таким образом, для каждого столбца в tableview создаем свой виджет for (int i = 0; i < m_scanning_model->columnCount(); i++) { ui->filter_layout->addWidget(create_filter_widget_by_scanning_column(i)); } В методе create_filter_widget_by_scanning_column определяем по индексу столбца, какой виджет будет создан для этого столбца. Сразу же к созданному виджету подключаем необходимые слоты. QWidget *MainWindow::create_filter_widget_by_scanning_column(const int &column) const { switch (column) { // пример текстового поля case 0: { auto widget = new QLineEdit(); widget->setValidator(new QRegExpValidator(QRegExp("[A-Za-z]{0,255}"))); connect(widget, &QLineEdit::textChanged, m_scanning_filter_model, &ScanningFilterModel::set_type); return widget; } // пример чекбокса case 7: { auto widget = new QCheckBox(); widget->setTristate(true); widget->setCheckState(Qt::CheckState::PartiallyChecked); connect(widget, &QCheckBox::stateChanged, m_scanning_filter_model, &ScanningFilterModel::set_enable); return widget; } default: return nullptr; } } Для красивого отображения виджетов (чтоб виджет был точно над столбцом, за который отвечает) подключаем изменение размера. Таким образом, при изменении размера окна или таблицы виджеты будут на своих местах. connect(ui->view_scan->horizontalHeader(), &QHeaderView::sectionResized, this, [this] (int index, int old_size, int new_size) { Q_UNUSED(old_size); auto widget = ui->filter_layout->itemAt(index)->widget(); widget->setMaximumWidth(new_size - 5); }); Вот то, что получилось в итоге:

Реализация сигналов и слотов в своём классе

#cpp #qt #qt5

Начал изучать Qt по книге М.Шлее и дошёл до сигналов и слотов, сам механизм понятен.
Попробовал написать простой класс реализующий этот механизм, но Qt Creator выводит
похожие ошибки  


  .../Error in " Util.asciify("build-...-Debug")":-1: error: TypeError:
  Property 'asciify' of object
  Core::Internal::UtilsJsExtension(0x155bb00) is not a
  function/.../tests.cpp:19: undefined reference to
  `SS_Primer::introduce_yourself()'


Класс компилируется

class SS_primer : public QObject {
        Q_OBJECT
    public:
        void send_signal() { emit introduce_yourself(); }
    public slots:
        void show_name() { std::cout << "I'm a slot" << std::endl; }
    signals:
        void introduce_yourself();
};


А этот код не хочет(

bool Test::SignalSlot() {
    SS_primer primer;
    QObject::connect( &primer, SIGNAL(introduce_yourself()), &primer, SLOT(show_name()) );
    primer.send_signal();
    return true;
}


Пробовал создавать объект класса через new, делал send_signal() static функцией принимающей
указатель. 
    


Ответы

Ответ 1



Вангую: Вы сделали все это дело в одном файле, не так ли? Вам следовало повнимательнее почитать Шлее, иначе бы Вы были бы вкурсе, что такое и, главное, как работает метакомпилятор. Короче разнести объявление и реализацию как положено и все заработает. PS Эти ошибки выводит не мифический Qt, а линкер - он вам прямо говорит, что не может найти ваш сигнал. Его действительно нет - можете даже создать динамическую библиотеку и проверить nm-ом. Это из-за того, что moc не отработал. PSS используйте новый синтаксис сигналов-слотов - он гораздо удобнее (за исключением перегруженный слотов) и при ошибках, связанных с неправильным подключением, вы получите больше корректной информации.