воскресенье, 5 октября 2014 г.

Эпопея с блокировкой файлов из интернета.

Скачал я как то раз клиент игрушки одной. Большой клиент, много exe и dll файликов. Однако запускаться он не пожелал.
Говорит не могу файлы запускать, изменять - они ж с непроверенного источника, из самого интернета.

Щелкнул свойства файла - вижу надпись:
"Этот файл  получен с другого компьютера и, возможно, был заблокирован с целью защиты компьютера." И кнопочка рядом - "Разблокировать".

Нажимая кнопочку - вроде сработало. Дальше всё по правилам - "Применить" и "Ок". Бац - не запускается. Я не в понятках, опять свойства открываю - а он опять заблокирован.

Пошёл в гугль правду искать. Искал искал, набрёл на утилиту снятия блокировки - Streams http://technet.microsoft.com/en-us/sysinternals/bb897440. Со всех файлов снимать блокировку умеет рекурсивно. Запускаю - ан нет, не может снять блокировку. А заместо объяснения почему - знаки вопросиков выводит.

Думал думал и надумал - а вдруг программисты микрософта сделали бяку - забыли что файл может быть с атрибутом "Только для чтения", и потому ошибку при разблокировке не выводят.

Ну, терять то нечего - открыл свойства всей папки, снял атрибут только для чтения - и вуаля, стали разблокироваться файлы. Написал я в консоли слова заветные - "streams -d -s %cd%" и разблокировались все файлы.

Вот такая вот незавидная история. А ведь пару метров нервов я потратил на не убирающуюся блокировку. Но даже программисты из микрософта могут ошибаться - забыли добавить обработку ошибки при снятии блокировки - в результате фиг поймёшь, что происходит.

PS Так же в процессе поисков выяснился механизм данной блокировки - в альтернативных потоках файла прописывается ini файл с меткой зоны, из которой был скачан. Увидеть их можно вызвав команду dir /r в папке с файлом.
Открыть эти файлы не представляется возможным - ругаются программы на некорректные именования файлов. Удалить тоже не получилось. Но есть мнение, что эти ограничения мнимые и прямой вызов функции удаления спокойно выполнит задачу.

четверг, 27 марта 2014 г.

Файловый диалог с таймаутом.

QFileDialog * dialog_;
void
{

dialog_ = new QFileDialog(this, "caption", ".", "*.*");
dialog_->setAcceptMode(QFileDialog::AcceptSave);

QTimer::singleShot(3000, this, SLOT(timeout()));
if (dialog_->exec() == QDialog::Accepted)
{
qDebug() << dialog_->selectedFiles();
}
}

void timeout()
{
dialog_->reject();
}

среда, 29 января 2014 г.

Цикл по GraphicsView(1). Создание сцены, view. Размещение стандартных элементов. Работа с элементами.

Приветствую. 
Речь пойдёт о таком интересном и мало описанном элементе Qt как QGraphicsView. 
Статьи будут писаться по мере нахождения у меня времени и продвижения программы, на примере которой я и собираюсь показать как надо использовать QGraphicsView. 
1) Создание сцены, view. Размещение стандартных элементов. Работа с элементами. 
2) Создание собственного элемента. Работа с собственным элементом. Создание редактора свойств. 
3) Взаимодействие элементов. Создание связи между двумя элементами. 
4) ?. 

Итак начнём. 

1) Создание сцены, view. Размещение стандартных элементов. Работа с элементами.

Создание сцены, view. Размещение стандартных элементов.
Работа с элементами.
Чтобы начать работать нам необходимы три компонента.
QGraphicsScene - scene или модель, которая хранит данные и работает с ними. 
QGraphicsView - view или элемент интерфейса, отображающий данные модели. Он является графическим компонентов и именно с ним работает пользователь.
QGraphics*Item - стандартные компоненты, которые могут быть помещены в scene и отображены view.
Для начала создаём проект Qt. Назовём его Graphics-lesson_1.

Переходим в graphics-lesson_1.h файл.
Добавляем инклуды:

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsLineItem>

Добавляем приватные поля нашему классу:

QGraphicsLineItem * lineItem_;
QGraphicsScene * scene_;
QGraphicsView * view_;

Так же добавляем приватный метод 

void initUI();

Переходим в graphics-lesson_1.cpp файл.
В конструктор класса добавляем строку 

initUi(); 

Пишем реализацию метода

void Graphics_lesson_1::initUI()
{
// создаём scene
scene_ = new QGraphicsScene;
// создаём view, делаем его центральным виджетом и устанавливаем сцену
view_ = new QGraphicsView();
setCentralWidget(view_);
view_->setScene(scene_);
// создаём объект линии и добавляем его в сцену.
lineItem_ = new QGraphicsLineItem(0,0, 250,250);
scene_->addItem(lineItem_);
}

Компилируем. Запускаем. Видим линию на белом фоне и ничего мы поделать с ней не можем.
Потому добавляем в метод iniUi в конец следующий строки

// разрешаем передвигать и выделять линию.
lineItem_->setFlag(QGraphicsItem::ItemIsMovable, true);
lineItem_->setFlag(QGraphicsItem::ItemIsSelectable, true);

Компилируем. Запускам. Теперь мы можем выделять и передвигать наш объект. Работа с другими стандартными объектами не отличается. Спасибо за внимание.

Архив 

Код 

graphics_lesson_1.cpp


#include "graphics_lesson_1.h"
Graphics_lesson_1::Graphics_lesson_1(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
initUI();
}
Graphics_lesson_1::~Graphics_lesson_1()
{
}
void Graphics_lesson_1::initUI()
{
// создаём scene
scene_ = new QGraphicsScene;
// создаём view, делаем его центральным виджетом и устанавливаем сцену
view_ = new QGraphicsView();
setCentralWidget(view_);
view_->setScene(scene_);
// создаём объект линии и добавляем его в сцену.
lineItem_ = new QGraphicsLineItem(0,0, 250,250);
scene_->addItem(lineItem_);
// разрешаем передвигать и выделять линию.
lineItem_->setFlag(QGraphicsItem::ItemIsMovable, true);
lineItem_->setFlag(QGraphicsItem::ItemIsSelectable, true);
}


graphics_lesson_1.h

#ifndef GRAPHICS_LESSON_1_H
#define GRAPHICS_LESSON_1_H
#include <QtGui/QMainWindow>
#include "ui_graphics_lesson_1.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsLineItem>
#include <QPushButton>
class Graphics_lesson_1 : public QMainWindow
{
Q_OBJECT
public:
Graphics_lesson_1(QWidget *parent = 0, Qt::WFlags flags = 0);
~Graphics_lesson_1();
private:
Ui::Graphics_lesson_1Class ui;
QGraphicsLineItem * lineItem_;
QGraphicsScene * scene_;
QGraphicsView * view_;
private:
void initUI();
};
#endif // GRAPHICS_LESSON_1_H
main.cpp
#include "graphics_lesson_1.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Graphics_lesson_1 w;
w.show();
return a.exec();
}

суббота, 11 января 2014 г.


Стайлшит для отбрасывания "снега" текстом. Выглядит отлично
Выделите и посмотрите на эффект.

СНЕГ

СНЕГ

СНЕГ


left: 28px;
width:100px;
text-align:center;
top:49px;
z-index: 5;
text-shadow: -2px -2px 2px white, -2px 2px 2px white, 2px 2px 2px white, 2px -2px 2px white;
color:#222;
font-size:30px;
font-weight:normal;

понедельник, 18 ноября 2013 г.

Сортировка QTreeWidget

Для сортировки по нужным вам критериям необходимо переопределить оператор сравнения у QTreeWidgetItem. Соответственно все добавляемые в QTreeWidget итемы должны быть вашего типа, иначе возможны неприятные последствия.

Ну и собственно код для примера (Сортировка COM портов ):
bool operator< ( const QTreeWidgetItem & other ) const
{
 QString textThis = this->text(1);
 QString textOther = other.text(1);

 if (textThis.contains("COM", Qt::CaseInsensitive) || textOther.contains("COM", Qt::CaseInsensitive))
 {
  textOther = textOther.replace("COM", "", Qt::CaseInsensitive);
  textThis = textThis.replace("COM", "", Qt::CaseInsensitive);
 }

 if (textThis.toInt() < textOther.toInt())
  return true;
 else
  return false;
}

вторник, 8 октября 2013 г.

Создание dll с сигнал-слотами внутри. Для NoQt приложений.

Приветствую всех заглянувших.

В этой статье я опишу свой взгляд и приведу пример создания DLL с системой сигнал-слотов Qt.

Внимание!!! Данная статья написана под Windows, IDE Visual Studio 2008 и микрософтовский же компилятор. Для использование данного метода под другими системами, вам придётся переписать платформозависимый код. Это создание потока в Dll и программа для загрузки Dll.

Дальше будет маленькое лирическое вступление. 

Иногда возникает необходимость написать программу без Qt. Требование ли это заказчика или архитектурная необходимость, но писать надо. И через некоторое время появляется проблема, которую можно легко и просто решить на Qt, но тяжело в NoQt проекте. 
Через некоторое время приходит мысль написать DLL с Си интерфейсов, из которой не видно и не слышно Qt. И вот тут начинается самое интересное.

Сигнал-слотовая система Qt основана на цикле событий. Если в приложении, использующем нашу DLL не используется Qt, то будут работать только сигналы-слоты с типом соединения "Qt::DirectConnection", так называемый прямой вызов методов. Но асинхронные интерфейсы при том работать не будут.

Начнём...

  • Создаём проект типа Qt Library. Назовём его "QLibrary". По умолчанию создастся проект динамической библиотеки. 

  • Следуем дальше указаниям менеджера и получаем три файла в проекте - QLibrary.cpp, QLibrary.h, QLibrary_global.h.
  • Первым делом убираем макрос "QLIBRARY_EXPORT" в объявлении класса. Экспортировать мы будем только функции интерфейса. 
  • Добавляем инклуды в проект. Нам нужны QObject, QFile, QCoreApplication.
  • По умолчанию класс не унаследован ни от кого. Для использования сигнал-слотов, нам необходимо добавить инклуды QObject, отнаследоваться от него же и добавить макрос Q_OBJECT в тело класса. Теперь мы можем использовать сигнал-слоты.



  • Добавим пару методов:

  1. void createFile (QString filename)
  2. void createFileSlot (QString filename)

      createFile у нас будет обычным методом класса, создающим файл в директории "C:/test".

      createFileSlot будет публичным слотом("public slots:") и будет добавлять к имени файла строку "signal". 
  • Добавляем сигнал void signalFileCreate(QString). 


На этом заканчиваем с .h файлом. Открываем .cpp.
  • Создаём глобальную переменную - указатель на наш класс "QLibrary * classPoint;". Через неё наш интерфейс будет взаимодействовать с классом.
  • В конструкторе прописываем соединение нашего сигнала с слотом. Указываем тип соединения - Qt::QueuedConnection. В ином случае соединение будет Qt::DirectConnect, что нам не надо. 
"connect( this, SIGNAL( signalFileCreate(QString) ), this, SLOT( createFileSlot(QString) ), Qt::QueuedConnection);"
  • Так же в конструкторе инициализируем указатель, тем самым обеспечивая возможность обращения к нашему классу.
"classPoint = this;"
  •  Пишем реализации методов класса.
  •  void QLibrary::createFile(QString filename)
    {
     QFile file(QString("c:/Test/%1").arg(filename));
     file.open(QIODevice::WriteOnly);
     file.write("test method!");
     emit signalFileCreate(filename);
    }
    void QLibrary::createFileSlot(QString filename)
    {
     QFile file(QString("c:/Test/%1 signal").arg(filename));
     file.open(QIODevice::WriteOnly);
     file.write("test public slot!");
    }
    


  • Метод createFile будет создавать файл "filename" и испускать сигнал. Слот createFileSlot будет вызываться сигнал-слотовой системой и создавать файл "filename signal". Содержимое тоже будет различаться, как видите.

  • Далее предстоит разобраться с функциями интерфейса и запуска цикла событий. Всего нам потребуются три функции:

  1. void createFile(char *) - будет вызывать метод createFile(Qstring) нашего класса.
  2. DWORD  MyThreadFunction( LPVOID lpParam )  - функция запуска отдельного потока для цикла событий (и для нашего класса)
  3. void instance() - будет создавать объект нашего класса и запускать поток цикла событий
    DWORD  MyThreadFunction( LPVOID lpParam )
    {
     QCoreApplication * app = NULL;
     int argc = 0;
     app = new QCoreApplication(argc, NULL);  
     QLibrary * dllClass =  new QLibrary();
       app->exec();
     return DWORD();
    }
    
    extern "C" QLIBRARY_EXPORT  void  instance()
    {
     LPDWORD lpThreadId = NULL;
     CreateThread(
      NULL,                   // default security attributes
      0,                      // use default stack size
      (LPTHREAD_START_ROUTINE)MyThreadFunction,       // thread function name
      NULL,          // argument to thread function
      0,                      // use default creation flags
      lpThreadId);
    }
    extern "C" QLIBRARY_EXPORT  void  createFile(char * inChar)
    {
     classPoint->createFile(QString::fromStdString(inChar));
    }

  • Экспортироваться будут только две функции - instance и createFile(char *).

  • Разберём что мы делаем. 
  1. Функция instance создаёт поток, в котором исполняется функция MyThreadFinction. В неё же мы создаём QCoreApplication, экземпляр своего класса и запускаем цикл обработки событий.
  2. Функция createFile будучи вызванной дёрнет метод createFile(QString). 

  • Компилируем - наслаждаемся отсутствием ошибок.


  • Имена экспортируемых методов могут меняться. Для пресечения этого необходимо добавить в проект .def файл со следующим содержимым

LIBRARY "QLibrary"
EXPORTS
    ; Explicit exports can go here
instance
createFile

  • Вот мы и создали Dll с циклом событий внутри.


  • Настало время проверить нашу dll. Для этого мы создадим маленькую C++ программу - DllLoader, код которой приведён ниже.
    #include "stdafx.h"
    #include "windows.h"
    #include <conio.h>
    #include <ctype.h>
    int _tmain(int argc, _TCHAR* argv[])
    {
     printf("The program demonstrates the use dll written on Qt. To work correctly, you need to create the directory \"C:/Test/\"\n");
     printf("Press any key to boot dll\n");
      _getch();
     HMODULE library = LoadLibrary(L"QLibrary.dll");
      if (!library)
     {
      printf("Not exists QLibrary.dll.");
      return 0;
     }
     printf("Dll loaded.\n");
     void (*dllInstance) (void);
     dllInstance = (void (*)(void))GetProcAddress(library, "instance");
      if (!dllInstance)
     {
      printf("Not export \"instance\" from DLL.");
      return 0;
     }
     dllInstance();
     printf("Dll initialized. Press any key to continue.\n");  _getch();
     void (*dllCreateFile) (char*);
     char* filename = "otherName";
     dllCreateFile = (void (*)(char*))GetProcAddress(library, "createFile");
     if (!dllCreateFile)
     {
      printf("Not export \"createFile\" from DLL.");
      return 0;
     }
     dllCreateFile(filename);
     printf("Work comlete. Check \"C:/Test/\" Press any key to exit. \n");  _getch();
     return 0;
    }
    



  • Программа загружает библиотеку, инициализирует наш класс вызовом instance и вызывает функцию createFile( "OtherName" ).



  • Для проверки нам необходимо создать директорию "C:/Test/" и скопировать нашу библиотеку в каталог DllLoader'a. Запускаем DllLoader, жмём любую клавишу два раза с небольшим промежутком и смотрим результат. В каталоге должны появиться файлы "otherName" и "otherName signal". 


Профит!!!

 Наша dll загружена, сигнал-слотовая система работает, приложение не знает ничего о Qt.

Архив с исходниками:
https://docs.google.com/file/d/0B_mrvcleB88yclZjSHhQVTQ5eXM/edit?usp=sharing
Зеркало:
https://dl.dropboxusercontent.com/u/62712483/Blog/QtDll_NoQtProg.ZIP

В архивах проекты для VS2008 и pro файл.

Совет:
При написании dll во всех connect'ах указывайте тип соединения. Из-за автоматического определения типа соединения могут возникать непонятные и опасные для программы ситуации. Так же возможно выпадение волос разработчика в процессе отладки и ранняя седина.

Предупреждение:
Загрузка в одно приложение нескольких экземпляров данной библиотеки может вызвать крах. Данная статья описывает лишь создание одного экземпляра Dll.
Если dll будет использоваться в Qt приложениях, необходимо добавить проверку на наличие уже существующего цикла событий.
Пожелания, предложения, замечания - в этот блог(вроде сообщения тут есть) или же на мыло work.bepec.sb@gmail.com.

Копирайтить копирайте, но ссылочку на источник прошу добавлять ^.^