среда, 29 июня 2011 г.

Перенос QtCreator проектов

У новичков которые хотят попробовать QtCreator часто возникают проблемы с переносом проектов в другой каталог или на другой компьютер. В следствии чего при сборке возникает проблема связанная с qmake. т.к вся сборка проходит в теневом режиме,т.е пользователь сам не вводит команды для сборки и прописывание путей к исходным файлам,то при переносе проекта все теневые настройки остаются для каталога до переноса
Пример :
Допустим при создании проекта в D:\Programming\qt\project\table. QtCreator автоматически создаст папку для сборки на том же уровне где и папка с исходниками,т.е это будет D:\Programming\qt\project\table-build-desktop например. Теперь мы решим перенести наш проект в другую папку,например D:\Programming\table и стереть все созданные им папки. После запуска *.pro файла и старта компиляции он нам скажет что не может найти папку для сборки и бла-бла-бла. К сожалению QtCreator туповат и не может сам создать папку для сборки проекта,поэтому приходиться там тыкать ему в "лицо" путь до папки для сборки,на скриншоте показано как это можно сделать. И ещё такой нюанс, если есть в пути русские буквы он тоже капризничает,так что не советую .

пятница, 24 июня 2011 г.

PyQt + GoogleAPI


Недавно заинтересовался языком python и решил сваять что-то. Так как я любитель Qt то и сваял gui с их помощью.
Задача была несложной.Ввести свои данные gmail и увидеть список своих документов на https://docs.google.com/.
Вот что получилось для примера:
main.py

from gui import *

if __name__ == '__main__':
import sys

app = QApplication(sys.argv)
window = Dialog()
sys.exit(window.exec_())

gui.py

#coding=windows-1251
from PySide.QtCore import *
from PySide.QtGui import *
import gdata.docs.service


class Dialog(QDialog):
def __init__(self):
super(Dialog,self).__init__()
# кодировка
QTextCodec.setCodecForCStrings(QTextCodec.codecForName("UTF-8"))
# добавляем всякие виджеты
self.setGui()
# конектим слоты
if(self.connect() == False):
print 'Connect do not work!'
def setGui(self):
# добавляем виджеты
self.labelEmail = QLabel('Email',self)
self.labelPassword = QLabel('Password',self)
self.lineEmail = QLineEdit(self)
self.linePassword = QLineEdit(self)
self.linePassword.setEchoMode(QLineEdit.Password)
self.list = QListWidget(self)
self.buttonGet = QPushButton('Get',self)
# создаём лаяут
self.grid = QGridLayout(self)
# кидаем туда виджеты
self.grid.addWidget(self.labelEmail,0,0)
self.grid.addWidget(self.labelPassword,1,0)
self.grid.addWidget(self.lineEmail,0,1)
self.grid.addWidget(self.linePassword,1,1)
self.grid.addWidget(self.list,2,0,1,2)
self.grid.addWidget(self.buttonGet,3,0,1,2)
def connect(self):
flag = False
flag = self.buttonGet.clicked.connect(self.on_buttonGetClicked)
return flag
# получаем список документов на доках хухла и добавляем в таблицу
def on_buttonGetClicked(self):
client = gdata.docs.service.DocsService()
client.ClientLogin(self.lineEmail.text(), self.linePassword.text())
documents_feed = client.GetDocumentListFeed()
for document_entry in documents_feed.entry:
self.list.addItem(document_entry.title.text)

воскресенье, 9 января 2011 г.

Неименованные каналы в windows


/* Программа клиент */
#include "stdafx.h"
#include "windows.h"
#include "conio.h"
#include "string.h"
#include "iostream"
#include "vector"
#define STOP -1 // Завершение цикла
using namespace std;
/*!
* Функция для проверки верности байт
*\param byteFirst - первое количество байт
*\param byteSecond - второе количество байт
*\return - 1 - количество байт совпадает, 0 - количество байт не совпадает
*/
bool checkByte(DWORD & byteFirst,DWORD & byteSecond)
{
if(byteFirst!= byteSecond)
return false;
return true;
}
int main(int argc, char* argv[])
{
// Если есть параметры у клиента(дескриптор на чтеление из канала)
if(argc<1)
{
// Завершение программы
cout << "Arrguments not enter" << endl;
_getch();
return -1;
}
int command = 0;
DWORD byteRead; // количество байт на чтение
DWORD byteNeedRead; // реальное количество прочитанах байт
// перевод аргумента из строковой формы в HANDLE
HANDLE hRead = (HANDLE)atoi(argv[0]);
// количесто байт на чтение
byteRead = sizeof(int);
// цикл до команды завершение цикла(-1)
while(command!=STOP)
{
// чтение данных из канала и проверка на верное считывание
if(ReadFile(hRead,(void*)&command,byteRead,&byteNeedRead,NULL) == FALSE || checkByte(byteRead,byteNeedRead) == false || byteNeedRead == 0)
{
// если даные не считались или считались неполностью
cout << "Fail with read" << endl;
_getch();
return -1;
}
// вывод считанных данныхх
cout << command << endl;
}
_getch();
return 0;
}


/* Программа сервер */
#include "stdafx.h"
#include "winsock2.h"
#include "windows.h"
#include "conio.h"
#include "string.h"
#include "iostream"
#include "vector"
#define STOP -1
using namespace std;
/*!
* Функция для проверки верности байт
*\param byteFirst - первое количество байт
*\param byteSecond - второе количество байт
*\return - 1 - количество байт совпадает, 0 - количество байт не совпадает
*/
bool checkByte(DWORD & byteFirst,DWORD & byteSecond)
{
if(byteFirst!= byteSecond)
return false;
return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hWrite; // дескриптор для записи
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE hRead; // дескриптор для чтения
int count = 0; // количество цифр для ввода
int *number; // цифры
wchar_t buffer[10]; // буфер для дискриптора
DWORD writeByte; // количество байт которые запишутся в канал
DWORD needWrite; // количество байт которые нужно записать в канал
SECURITY_ATTRIBUTES attr;
attr.bInheritHandle = TRUE; // наследуемый дескриптор
attr.lpSecurityDescriptor = NULL;
attr.nLength = sizeof(SECURITY_ATTRIBUTES);
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
// Создание неименновоного канала
if(!CreatePipe(&hRead,&hWrite,&attr,1024))
return -1;
// Перевод дескриптора на чтение в строковую форму
_itow((int)hRead,buffer,10);
// Создание нового процесса
// Параметром командной строки является дескриптор на чтение
if(!CreateProcess(L"child.exe",buffer,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi))
{
// в случае ошибка закрытие дескрипторы на чтение и на запись
CloseHandle(hRead);
CloseHandle(hWrite);
printf( "CreateProcess failed (%d)\n", GetLastError() );
_getch();
return -1;
}
// Ввод количество цифр и самих цифр
cout << "Enter count number: ";
scanf("%d", &count);
number = (int*)calloc(sizeof(int),count);
for(int i = 0 ; i < count; i++)
cin >> number[i];
writeByte = sizeof(int); // количество байт которые будут записываться в канал
// Если произошла запись в канал, записалось нужное количество байт и они записались
if(WriteFile(hWrite,(void*)&count,writeByte,&needWrite,NULL) == FALSE || checkByte(writeByte,needWrite) == false || needWrite ==0)
{
// если была ошибка
cout << "Data was break" << endl;
goto exit;
}
// запись чисел в канал и проверка как и в прошлом комментарии
for(int i = 0 ; i < count ; i++)
if(WriteFile(hWrite,(void*)&number[i],writeByte,&needWrite,NULL) == FALSE || checkByte(writeByte,needWrite) == false || needWrite ==0)
{
cout << "Data was break" << endl;
break;
}
// после отправки всех чисел отправка комманды на завершение,так же с проверкой
count = -1;
if(WriteFile(hWrite,(void*)&count,writeByte,&needWrite,NULL) == FALSE || checkByte(writeByte,needWrite) == false || needWrite ==0)
{
cout << "Data was break" << endl;
goto exit;
}
exit:
// Завершение всех процесов и закрытие программы
free(number);
FlushFileBuffers(hRead);
FlushFileBuffers(hWrite);
CloseHandle(hRead);
CloseHandle(hWrite);
_getch();
return 0;
}

вторник, 28 декабря 2010 г.

TLS. Локальное хранилище потока.

Идея проста.Если нужна использовать глобальные переменные для потока,то как решить проблему использование одной и той же памяти разными потоками.Есть решение - TLS.
В небольшом примере всё будет видно.

#include "stdafx.h"
#include "iostream"
#include "string.h"
#include "windows.h"
using namespace std;

// Глобальные переменные
int tls_int;
char simple_char[25];
DWORD tls_char;
/*!
* Потоковая функция
* \param param - параметр потоковой функции
* \return - результат работы потоковой функции
*/
DWORD WINAPI ThreadFunc(LPVOID param);
/*!
* Потоковая функция использующая технологию TLS
* \param param - параметр потоковой функции
* \return - результат работы потоковой функции
*/
DWORD WINAPI ThreadFuncTls(LPVOID param);
/*!
* Главная функция программы
*/
int _tmain(int argc, _TCHAR* argv[])
{
char variant[20];
DWORD threadId;
cout << "Enter method - ";
cin >> variant;
if (!strcmp(variant,"simple"))
{
CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&threadId);
CreateThread(NULL,0,ThreadFunc,(LPVOID)2,0,&threadId);
Sleep(10000);
}
if (!strcmp(variant,"tls"))
{
/*
Прототип
DWORD WINAPI TlsAlloc(void);
TlsAlloc - резервирует свободный блок памяти
который будет принаджежать только потоку внутри
которого она вызывалась.
Возвращает индекс ячейки зарезервированной памяти,
если память невозможно выделить возвращает TLS_OUT_OF_INDEXES .
*/
tls_int = TlsAlloc();
tls_char = TlsAlloc();
CreateThread(NULL,0,ThreadFuncTls,(LPVOID)1,0,&threadId);
CreateThread(NULL,0,ThreadFuncTls,(LPVOID)2,0,&threadId);
Sleep(10000);
TlsFree(tls_int);
TlsFree(tls_char);
}
return 0;
}
DWORD WINAPI ThreadFunc(LPVOID param)
{
// юзаем глобальные переменные
tls_int = (int)param;
strcpy(simple_char,"array symbol");
Sleep(tls_int);
cout << "Parameter = " << tls_int << endl;
cout << "string : " << simple_char << endl;
return 0 ;
}
DWORD WINAPI ThreadFuncTls(LPVOID param)
{
/*
Прототип
BOOL WINAPI TlsSetValue(
__in DWORD dwTlsIndex,
__in LPVOID lpTlsValue
);
TlsSetValue - Можно догадаться функция заносит
в выделеную локальную память потока значение.
Первым параметром является индекс выделенной ячейки памяти,
т.е то что нам вернула функция TlsAlloc.
Вторым же значение которое надо занясти в память.
*/
TlsSetValue(tls_int,param);
char buffer[25];
strcpy(buffer,"array symbol");
TlsSetValue(tls_char,buffer);
/*
Прототип
LPVOID WINAPI TlsGetValue(__in DWORD dwTlsIndex);
TlsGetValue - Функция возвращает значение из заразервированной
ячейки памяти.
Параметром является индекс зарезервированной ячейки памяти.
*/
Sleep((DWORD)TlsGetValue(tls_int));
cout << "Parametr = " << (DWORD)TlsGetValue(tls_int) << endl;
cout << "string : " << (char *)TlsGetValue(tls_char)<< endl;
return 0;
}

воскресенье, 28 ноября 2010 г.

Функция с переменным количеством параметров

Hi!Для операционных систем для реализации синхронизации разных запросов,используется разные фишки: мьютексы,семафоры,событи и т.д. Я выбрал события,и для разных функций использовалось переменное количество переменных для синхронизации секций.И решать проблему не хочется Ctrl+c, Ctrl+v)Решил написать функцию с переменным количеством параметров:)

HANDLE * hmains(HANDLE handle,...)
{
va_list par; //лист с параметрами*
int count = -1; // количество переменных
int size; // вспомогательная переменная
HANDLE *cp = &handle; // Начало в памяти для всех переменных функции
va_start(par,handle); // присваиваем листу начало для переменных
while(cp = va_arg(par,HANDLE*)) // подсчёт количество переменных
count++;
size = count;
va_start(par,handle);
HANDLE *hmain = new HANDLE[count]; // Память под массив переменных
hmain[0] = handle; // Первая переменная в массиве
while(cp = va_arg(par,HANDLE *)) // Заполняем массив оставшимися переменными
{
if(size==1)
break;
hmain[count - size +1] = cp; // непосредственно заполнение
size--;
}
return hmain; // Возвращаем массив переменных
}

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

суббота, 13 ноября 2010 г.

Использование потоковых функций в классе

Итак я столкнулся с 1 проблемой,что у меня был класс Server,который работал всего с 1 клиентом.Но это не нормально когда сервер всего для 1 клиента,поэтому пришлось сделать потоковую функция для обработки запросов клиентов:)А т.к при вызове функции CreateThread протокол у функции должен быть иным,чем у функции класса,происходил FAIL.Пришлось выкручиваться.
Покажу реализацию на простом примере.
Простой класс.

class Data;
struct dataStruct
{
Data *data;
HANDLE handle;
};
class Data
{
public:
Data()
:xData(10),
yData(10)
{

}
~Data()
{

}
int & x(){return this->xData;}
int & y(){return this->yData;}
int sum()const{return xData+yData;}
/*Запиливаем потоковую функцию*/
friend DWORD WINAPI thread(void *param)
{
dataStruct *data = (dataStruct*)param;
int count = data->data->sum();
for(int i=0;i< count ; i++)
{
Sleep(100);
cout << data->data->sum() << endl;
data->data->x()++;
data->data->y()++;
}
return 0;
}
private:
int xData;
int yData;
};

А теперь просто в main будем запускать.

void main()
{
for(int i = 0; i<10; i++)
{
Data data;
dataStruct rec;
rec.data = &data;
Sleep(1000);
rec.handle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread,(LPVOID)&rec ,0,0);
}
Sleep(10000);
}

вторник, 2 ноября 2010 г.

Qt + MySQL

Начали проходить БД, и дело, конечно, не обошлось без MySQL:) Так как я любитель Qt, То GUI конечно хотелось бы написать на нём и соответственно взаимодействие с БД осуществить с помощью этой библиотеки.
Все, я думаю, читали, что для использования возможностей API MySQL надо просто подключить нужный драйвер, в нашем случае это QMYSQL. Делается это очень просто.
QSqlDatabase  & db = QSqlDatabase::addDatabase("QMYSQL","base");

Но при запуске может возникнуть проблема - библиотека говорит нам, что драйвер QMYSQL не найден. А ведь действительно, откуда qt знает, где наш sql  Как говорится – «телепаты в отпуске» :)
Что же нам делать в этом случае? Ответ вполне прост, надо нам собрать qt с нужными для нас модулями, в частности для mysql. Делать мы это будем в любимой всеми командной строке.
Так как я не любитель стандартной консоли в windows, то пользуюсь программой “Console”. Название вполне оригинальное:)
Итак, что там нужно: во-первых, собираться будем для visual studio 2008, поэтому будем пользоваться её утилитой Visual Studio 2008 Command Prompt.
Или можно ввести в консоли следующее:
%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"" x86



p.s путь к батнику у вас может быть иным .
Итак, теперь осталось верно собрать кутишечку:)
Для этого переходим в консоли в папку с Qt и набираем:
configure –debug-and-release –platform win32-msvc2008 –qt-sql-mysql



Дальше пойдёт сборка



После окончания сборки, мы радостные по идее должны зайти в src\sql и выполнить nmake, но не тут-то было:) Нам скажут , что он не понимает где лежат сами либы. Придётся прикручивать всё вручную, ну как обычно:)
Находим либы и инклуды в папке с MySQL. По идее, они должны лежать в MySQL\MySQL Server\lib .
Так вот, смысл в том, что мы вручную указываем в исходниках qt путь до этой папки с файлом mysql.h.
В моём случае получилось так :



Теперь снова заходим в src\sql и делаем nmake



Теперь всё должно пройти нормально:)
Удачи. Если что пишите на почту chexov34@gmail.com по возможности отвечу.
Отдельное спасибо Мамонтову Дмитрию http://vkontakte.ru/id6925882. Он просто мегамозг и задрот :)