Совсем недавно в блоге разработчиков Qt появилась информация о том что доступна Technology Preview версия Qt для устройств на платформе S60.

На данный момент доступны модули:

  • QtCore – all the non-Gui classes
  • QtGui – painting and widgets
  • QtNetwork – TCP, HTTP and all that
  • QtTest – unit testing

Анонсирована поддержка устройств S60 3.1 и выше.

Загрузить Qt for S60 можно здесь.

Дополнительную информацию о Qt for S60 можно узнать здесь.

Также на YouTube можно найти видео-ролик с демонстрацией работы Qt на устройствах S60:

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

Leave a Reply

Your email address will not be published. Required fields are marked *

К.

Как получить изображение с камеры в Windows Mobile

Итак, камера… Windows Mobile версии 5 и выше предоставляет API для работы с камерой мобильного телефона. Что значит “для работы”? А значит это то, что кроме возможности получения изображения, которая будет рассмотрена в этом посте, предоставляемое API позволяет, также, осуществить захват видео любого формата, поддерживаемого Windows Media Player. Все эти радости жизни доступны посредством функции

HRESULT SHCameraCapture (PSHCAMERACAPTURE pshcc);


Документация по функции SHCameraCapture доступна в MSDN здесь, а описание структуры SHCAMERACAPTURE, которая передается в параметр функции, доступно здесь.
А сейчас мы рассмотрим небольшой пример использования выше указанной функции для получения изображения в формате JPG с камеры мобильного устройства. Ниже представлен код функции, которая отображает диалог для работы с камерой, который позволяет сохранить изображение с камеры в файл. После успешного заврешения работы диалога параметр result будет содержать путь к файлу с изображением.

static bool DoCaptureImage(LPCTSTR captureDir, LPCTSTR defaultFileName, 
						   LPTSTR result, DWORD resultSize, HWND parent)
{
	do
	{
		HRESULT         hResult;
		SHCAMERACAPTURE shcc;

		// Заполняем поля структуры SHCAMERACAPTURE.
		ZeroMemory(&shcc, sizeof(shcc));
		shcc.cbSize				= sizeof(shcc);
		shcc.hwndOwner			= parent;
		shcc.pszInitialDir		= captureDir;
		shcc.pszDefaultFileName	= defaultFileName;
		shcc.pszTitle			= TEXT("Capture Image");
		shcc.nResolutionWidth	= 0;
		shcc.nResolutionHeight	= 0;
		shcc.nVideoTimeLimit	= 0;
		shcc.Mode				= CAMERACAPTURE_MODE_STILL;
		shcc.StillQuality		= CAMERACAPTURE_STILLQUALITY_HIGH;

		// Отображаем диалог для работы с камерой
		hResult = SHCameraCapture(&shcc);
		if(hResult != S_OK) break;
		StringCchCopy(result, resultSize, shcc.szFile);
		return true;
	}
	while(false);
	return false;
}

Идем дальше… Теперь рассмотрим, как все описанное выше можно поиспользовать с wxWidgets и получить изображение в виде переменной wxImage:

static wxImage CaptureImage(WXWidget parent = NULL)
{
	do
	{
		// Создаем временное имя файла
		wxString tempFileName = wxFileName::CreateTempFileName(wxT("capture_"));
		// Т.к. wxWidgets по умолчанию добавляет расширение .tmp, 
		// а нам надо бы .jpg, то добавляем расширение руками
		tempFileName += wxT(".jpg");

		// Копируем путь к каталогу с временным файлом в буфер 
		TCHAR captureDir[MAX_PATH];
		StringCchCopy(captureDir, MAX_PATH, wxPathOnly(tempFileName).GetData());

		// Копируем имя файла в буфер
		TCHAR defaultFileName[MAX_PATH];
		StringCchCopy(defaultFileName, MAX_PATH, wxFileName(tempFileName).GetFullName());

		TCHAR fileNameBuffer[MAX_PATH];

		// Если ошибка то выходим...
		if (!DoCaptureImage(captureDir, defaultFileName, 
			fileNameBuffer, MAX_PATH, (HWND)parent)) break;
		// Получаем имя файла в переменную wxString
		wxString resultFileName(fileNameBuffer);
		// Загружаем отснятое изображение
		wxImage result(resultFileName, wxBITMAP_TYPE_ANY);
		// Удаляем временный файл
		if(wxFileExists(resultFileName)) wxRemoveFile(resultFileName);
		// Если изображение было загружено неудачно, то возвращаем false
		if(!result.IsOk()) break;
		// Если все нормально, то возвращаем true
		return result;
	}
	while(false);
	return wxNullImage;
}

Вот, такой нехитрый финт ушами позволяет нам получить изображение в виде переменной wxImage, которую можно использовать в GUI-приложении, например как-то так:

void wxCameraTestWMMainFrame::OnOPENClick( wxCommandEvent& event )
{
	wxImage capturedImage = 
		wxWMCameraCapture::CaptureImage(GetHandle());
	if(capturedImage.IsOk())
	{
		double controlScale = 
			(double)m_StaticBitmap->GetClientSize().GetHeight()/
			(double)m_StaticBitmap->GetClientSize().GetWidth();
		double imageScale = 
			(double)capturedImage.GetHeight() / 
			(double)capturedImage.GetWidth();
		double zoomLevel(1.0);
		if(imageScale > controlScale)
		{
			zoomLevel = (double)m_StaticBitmap->GetSize().GetHeight() / (double)capturedImage.GetHeight();
		}
		else
		{
			zoomLevel = (double)m_StaticBitmap->GetSize().GetWidth() / (double)capturedImage.GetWidth();
		}
		wxImage scaledImage = capturedImage.Scale(capturedImage.GetWidth() * zoomLevel,
			capturedImage.GetHeight() * zoomLevel);
		m_StaticBitmap->SetBitmap(wxBitmap(scaledImage));
	}
}

The конец.

Исходный код примера и проект для Visual Studio 2008 можно скачать здесь:
Пример получения изображения с камеры под Windows Mobile

Р.

Работаем с Flickr на .NET Compact Framework

Что-то все больше меня уносит в сторону ПО, работающего со всякими online-сервисами. Вон было недавно о Google Translate и Yahoo! Maps, а сегодня буду рассказывать о том как работать с сервисом хостинга фотографий Flickr на .NET Compact Framework.

Итак, предметом нашего внимания сегодня будет библиотека Flickr.NET, которую можно найти на CodePlex. Flickr.NET – это библиотека с открытым исходным кодом, есть версия для .NET Compact Framework. Вот ее мы и будем использовать.

Для начала Создаем новый проект для Smart Device на C#.

Затем распаковываем исходный код Flickr.NET в папку с созданным решением (рядом с папкой, в которой находится исходный код только что созданного проекта).

После этого добавляем проект FlickrNetCF в решение

Добавляем ссылку на проект FlickrNetCF в список зависимостей нашего приложения (с помощью Project -> Add Reference).

Ну вот, теперь можно работать.

Рисуем вот такую форму:

На форме:

  • Поле ввода запроса для поиска изображений
  • Кнопка поиска
  • Список (ListView) для просмотра информации о найденных изображениях
  • Кнопка открытия формы для просмотра полноразмерного изображения

Теперь можно заняться, собсьвенно, работой с Flickr. Для работы с сервисом в библиотеке FlickrNetCF есть класс Flickr. Для работы ему необходимы:

  • Application Key
  • Secret Key

Получить эти два ключа можно вот по этому адресу.

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

using FlickrNet;
...
Flickr _flickr = new Flickr();
_flickr.ApiKey = "<здесь будет ваш API Key>";
_flickr.ApiSecret = "<здесь будет ваш Secret Key>";

Теперь нам неплохо бы обеспечить поиск изображений и отображение превью, а также информации об изображении. Загрузка превью – процесс длительный, поэтому обрабатывать информацию, полученную от сервиса будем в отдельном потоке:

Thread _downloadThread;
string _searchText = "";
...
private void StartDownloadingPreviws()
    {
    if (_downloadThread != null)
    {
        _downloadThread.Abort();
    }
    photoInfoListView.Items.Clear();
    photoThumbnailList.Images.Clear();
    _searchText = searchTextBox.Text;
    _downloadThread = new Thread(new ThreadStart(DownloadPhotosDelegate));
    _downloadThread.Start();
}

Для поиска изображений в классе Flickr предусмотрен метод PhotosSearchText(), который в качестве параметра принимает строку поискового запроса. Именно этот метод мы и будем использовать для получения информации о найденных фотографиях. Метод PhotosSearchText() возвращает объект класса Photos, из которого мы можем получить всю информацию о найденных изображениях (свойство PhotoCollection класса Photos возвращает массив объектов Photo, каждый из которых содержит полную информацию об одном изображении, например, название, ссылку на превью, ссылку на полноразмерное изображение и др.)

void DownloadPhotosDelegate()
{
    try
    {
        Photos _photos = _flickr.PhotosSearchText(_searchText);
        foreach (Photo photo in _photos.PhotoCollection)
        {
            MemoryStream photoStream = new MemoryStream();
            WebRequest request = HttpWebRequest.Create(photo.SquareThumbnailUrl);
            WebResponse response = request.GetResponse();
            MemoryStream stream = new MemoryStream();
            Stream responseStream = response.GetResponseStream();
            int readCount = 0;
            byte[] buffer = new byte[1024];
            while ((readCount =
                responseStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                stream.Write(buffer, 0, readCount);
            }
            response.Close();

            Bitmap bmp = new Bitmap(stream);
            string originalURL = photo.LargeUrl;
            string title = photo.Title;
            Invoke(new AddItemDelegate(AddItemToList),
                new object[] { bmp, title,  originalURL})
        }
    }
    catch (ThreadAbortException)
    {
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Т.к. в .NET неглавный поток не может работать с элементами GUI, то добавление превью изображений в список нам необходимо делать в отдельном методе, который будет вызываться посредством Invoke():

Invoke(new AddItemDelegate(AddItemToList), new object[] { bmp, title,  originalURL});
...
private delegate void AddItemDelegate(Bitmap bmp, string name, string originalURL);

Bitmap ResizeBitmap(Bitmap b, System.Drawing.Size size)
{
    Bitmap result = new Bitmap(size.Width, size.Height);
    using (Graphics g = Graphics.FromImage((Image)result))
        g.DrawImage(b, 
            new Rectangle(0, 0, result.Width, result.Height), 
            new Rectangle(0, 0, b.Width, b.Height), 
            GraphicsUnit.Pixel);
    return result;
}

void AddItemToList(Bitmap bmp, string name, string originalURL)
{
    try
    {
        photoThumbnailList.Images.Add(bmp);
        ListViewItem newItem = new ListViewItem(name);
        newItem.ImageIndex = photoThumbnailList.Images.Count - 1;
        newItem.Tag = originalURL;
        photoInfoListView.Items.Add(newItem);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

ОК, с главной формой пока закончили. теперь приступим к созданию формы для просмотра полноразмерного изображения.
Добавляем в проект новую форму. На нее кладем Panel (Dock = Fill, AutoScroll = true), на Panel кладем PictureBox.
Теперь нам нужно обеспечить загрузку изображения в отдельном потоке. Подход приблизительно такой же как и в главной форме:

private Thread _downloader;
private string _imageURL = "";
Bitmap _sourceBitmap;

public string ImageURL
{
    get
    {
        return _imageURL;
    }
    set
    {
        _imageURL = value;
    }
}

public ImageViewForm()
{
    InitializeComponent();
}

void OnImageDownloaded(Bitmap bmp)
{
    _sourceBitmap = bmp;
    imageBox.Image = bmp;
    imageBox.Width = bmp.Width;
    imageBox.Height = bmp.Height;
}

private delegate void ImageDownloadedDelegate(Bitmap bmp);

void DownloadImage()
{
    try
    {
        do
        {
            if (ImageURL == null || ImageURL.Length == 0) break;
            WebRequest request = HttpWebRequest.Create(ImageURL);
            WebResponse response = request.GetResponse();
            Bitmap bmp = new Bitmap(response.GetResponseStream());
            response.Close();
            Invoke(new ImageDownloadedDelegate(OnImageDownloaded),
                new object[] { bmp });
        }
        while (false);
    }
    catch (ThreadAbortException)
    {
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

public void StartDownloading()
{
    _downloader = new Thread(new ThreadStart(DownloadImage));
    _downloader.Start();
}

private void ImageViewForm_Closing(object sender, CancelEventArgs e)
{
    if (_downloader != null)
    {
        _downloader.Abort();
    }
}

Теперь пишем обработчик нажатия кнопки открытия формы просмотра полноразмерного изображения в главной форме

private void showPictureButton_Click(object sender, EventArgs e)
{
    do
    {
        if (photoInfoListView.SelectedIndices.Count == 0) break;
        ListViewItem item = photoInfoListView.Items[photoInfoListView.SelectedIndices[0]];
        if (item == null) break;
        string url = (item.Tag as string);
        if (url == null || url.Length == 0) break;
        ImageViewForm dlg = new ImageViewForm();
        dlg.ImageURL = url;
        dlg.StartDownloading();
        dlg.ShowDialog();
    }
    while (false);
}

Ну вот, собственно и все. В результате мы получим вот такое приложение:


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