SyntaxHighlighter

2013年5月13日月曜日

[EDK]BMPデコーダを作成

前回投稿記事のBMPデコード部分があまりにもアレだったので、ここを参考にBMPデコーダを作成しました。

ソース

OS/2形式、Windows形式の1, 4, 8, 24, 32bit BMPに対応するように作ってみました。
あまりお目にかかる機会のないであろう、ランレベル圧縮やビットフィールドについては対応せず。


大したことやってないですが、以下概要。

Bitmapファイルの各種情報を解析、保持するために、構造体を定義。
#define  MAX_PALETTES  256

#pragma pack(1)
typedef struct {
  CHAR8   Type[2];
  UINT32  FileSize;
  UINT32  Reserved;
  UINT32  Offset;
} BITMAP_FILE_HEADER;

typedef struct {
  UINT32  HeaderSize;
  INT16   Width;
  INT16   Height;
  UINT16  Planes;
  UINT16  BitCount;
} BITMAP_CORE_HEADER;

typedef struct {
  UINT32  HeaderSize;
  INT32   Width;
  INT32   Height;
  UINT16  Planes;
  UINT16  BitCount;
  UINT32  Compression;
  UINT32  ImageSize;
  INT32   XPixPerMeter;
  INT32   YPixPerMeter;
  UINT32  ClrUsed;
  UINT32  ClrImportant;
} BITMAP_INFO_HEADER;

typedef struct {
  UINT8  Red;
  UINT8  Green;
  UINT8  Blue;
} BITMAP_COLOR_PALETTE;

typedef struct {
  VOID                  *FileData;
  VOID                  *InfoData;
  VOID                  *PaletteData;
  VOID                  *ImageData;
  UINTN                 FileSize;
  BOOLEAN               IsOs2;
  UINTN                 Width;
  UINTN                 Height;
  BOOLEAN               Invert;
  UINTN                 BitCount;
  UINTN                 Compression;
  UINTN                 UsedPalette;
  BITMAP_COLOR_PALETTE  Palette[MAX_PALETTES];
} BITMAP_INFORMATION;
#pragma pack()

Bitmapファイルのヘッダを読んで、BITMAP_INFORMATION構造体へ情報を入れておきます。
OS/2形式とWindows形式ではヘッダの大きさが異なるので、Information HeaderのSize情報を使用して場合分け。
STATIC
EFI_STATUS
ParseFileHeader (
  IN OUT BITMAP_INFORMATION  *BitmapInfo
)
{
  BITMAP_FILE_HEADER  *FileHeader;

  if (BitmapInfo->FileSize < sizeof (BITMAP_FILE_HEADER)) {
    return EFI_BAD_BUFFER_SIZE;
  }
  FileHeader = (BITMAP_FILE_HEADER*) BitmapInfo->FileData;
  if (!(FileHeader->Type[0] == 'B' && FileHeader->Type[1] == 'M')) {
    return EFI_UNSUPPORTED;
  }

  BitmapInfo->FileSize = MIN (BitmapInfo->FileSize, FileHeader->FileSize);
  BitmapInfo->InfoData  = FileHeader + 1;
  BitmapInfo->ImageData = (UINT8*) BitmapInfo->FileData + FileHeader->Offset;

  return EFI_SUCCESS;
}


STATIC
EFI_STATUS
ParseInformationHeader (
  IN OUT BITMAP_INFORMATION  *BitmapInfo
)
{
  BITMAP_CORE_HEADER  *CoreHeader;
  BITMAP_INFO_HEADER  *InfoHeader;

  CoreHeader = (BITMAP_CORE_HEADER*) BitmapInfo->InfoData;
  if (BitmapInfo->FileSize < ((UINTN) BitmapInfo->ImageData - (UINTN) BitmapInfo->FileData)) {
    return EFI_BAD_BUFFER_SIZE;
  }

  if (CoreHeader->HeaderSize == sizeof (BITMAP_CORE_HEADER)) {
    BitmapInfo->IsOs2 = TRUE;
    BitmapInfo->Width = CoreHeader->Width;
    if (CoreHeader->Height >= 0) {
      BitmapInfo->Height = CoreHeader->Height;
      BitmapInfo->Invert = FALSE;
    } else {
      BitmapInfo->Height = CoreHeader->Height * (-1);
      BitmapInfo->Invert = TRUE;
    }
    BitmapInfo->BitCount = CoreHeader->BitCount;
    BitmapInfo->Compression = 0;
    if (BitmapInfo->BitCount > 8) {
      BitmapInfo->UsedPalette = 0;
    } else {
      BitmapInfo->UsedPalette = 1 << BitmapInfo->BitCount;
    }

    BitmapInfo->PaletteData = CoreHeader + 1;
  } else if (CoreHeader->HeaderSize == sizeof (BITMAP_INFO_HEADER)) {
    BitmapInfo->IsOs2 = FALSE;
    InfoHeader = (BITMAP_INFO_HEADER*) BitmapInfo->InfoData;
    BitmapInfo->Width    = InfoHeader->Width;
    if (InfoHeader->Height >= 0) {
      BitmapInfo->Height   = InfoHeader->Height;
      BitmapInfo->Invert   = FALSE;
    } else {
      BitmapInfo->Height   = InfoHeader->Height * (-1);
      BitmapInfo->Invert   = TRUE;
    }
    BitmapInfo->BitCount = InfoHeader->BitCount;
    BitmapInfo->Compression = InfoHeader->Compression;
    if (BitmapInfo->BitCount > 8) {
      BitmapInfo->UsedPalette = 0;
    } else {
      if (InfoHeader->ClrUsed == 0) {
        BitmapInfo->UsedPalette = 1 << BitmapInfo->BitCount;
      } else {
        BitmapInfo->UsedPalette = InfoHeader->ClrUsed;
      }
    }

    BitmapInfo->PaletteData = InfoHeader + 1;
  } else {
    return EFI_UNSUPPORTED;
  }

  return EFI_SUCCESS;
}

1, 4, 8bitカラーBitmapの場合、カラーパレットを利用するので、パレット情報を保持。
ここでも、OS/2形式とWindows形式でパレットの使い方が違う(パレットサイズがOS/2は24bit、Windowsは32bit)ので場合分けを挟んでいます。
STATIC
EFI_STATUS
GetPalette (
  IN OUT BITMAP_INFORMATION  *BitmapInfo
)
{
  UINTN  Index;
  struct {
    UINT8  Blue;
    UINT8  Green;
    UINT8  Red;
  } *PaletteDataOs2 = BitmapInfo->PaletteData;
  struct {
    UINT8  Blue;
    UINT8  Green;
    UINT8  Red;
    UINT8  Reserved;
  } *PaletteDataWin = BitmapInfo->PaletteData;

  if (BitmapInfo->IsOs2) {
    for (Index = 0; Index < BitmapInfo->UsedPalette; Index++, PaletteDataOs2++) {
      BitmapInfo->Palette[Index].Red   = PaletteDataOs2->Red;
      BitmapInfo->Palette[Index].Green = PaletteDataOs2->Green;
      BitmapInfo->Palette[Index].Blue  = PaletteDataOs2->Blue;
    }
  } else {
    for (Index = 0; Index < BitmapInfo->UsedPalette; Index++, PaletteDataWin++) {
      BitmapInfo->Palette[Index].Red   = PaletteDataWin->Red;
      BitmapInfo->Palette[Index].Green = PaletteDataWin->Green;
      BitmapInfo->Palette[Index].Blue  = PaletteDataWin->Blue;
    }
  }

  return EFI_SUCCESS;
}

最後に、ピクセルの情報をEFI_GRAPHICS_OUTPUT_BLT_PIXELに変換しました。
Bitmapファイルは通常、左下から右上に向かってデータが格納されているため、Y軸をデクリメントさせて処理しています。
また、各行のサイズはDWORDアラインされている必要が有るため、途中アライメント処理を挟みました。
STATIC
EFI_STATUS
FillBltBufferWithPalette (
  IN     BITMAP_INFORMATION            *BitmapInfo,
  IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer
)
{
  UINT8  *BitmapData;
  UINTN  XIndex;
  UINTN  YIndex;
  UINTN  PixPerByte;
  UINTN  Pos;
  UINTN  BitPos;
  UINTN  BltPos;
  UINT8  PaletteIndex;

  PixPerByte = 8 / BitmapInfo->BitCount;

  BitmapData = BitmapInfo->ImageData;

  for (YIndex = BitmapInfo->Height; YIndex > 0; YIndex--) {
    for (XIndex = 0; XIndex < BitmapInfo->Width;) {
      Pos    = (YIndex - 1) * ALIGN_VALUE (BitmapInfo->Width / PixPerByte, 4) + (XIndex / PixPerByte);
      for (BitPos = 0; BitPos < 8; BitPos += BitmapInfo->BitCount, XIndex++) {
        if (BitmapInfo->Invert) {
          BltPos = (YIndex - 1) * BitmapInfo->Width + XIndex;
        } else {
          BltPos = (BitmapInfo->Height - YIndex) * BitmapInfo->Width + XIndex;
        }
        PaletteIndex = BitFieldRead8(BitmapData[Pos], (7 - BitPos) - (BitmapInfo->BitCount - 1), (7 - BitPos));

        BltBuffer[BltPos].Red      = BitmapInfo->Palette[PaletteIndex].Red;
        BltBuffer[BltPos].Green    = BitmapInfo->Palette[PaletteIndex].Green;
        BltBuffer[BltPos].Blue     = BitmapInfo->Palette[PaletteIndex].Blue;
        BltBuffer[BltPos].Reserved = 0;
      }
    }
  }

  return EFI_SUCCESS;
}


STATIC
EFI_STATUS
FillBltBufferWithoutPalette (
  IN     BITMAP_INFORMATION            *BitmapInfo,
  IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer
)
{
  UINT8  *BitmapData;
  UINTN  XIndex;
  UINTN  YIndex;
  UINTN  BytePerPix;
  UINTN  Pos;
  UINTN  BltPos;

  BitmapData = BitmapInfo->ImageData;
  BytePerPix = BitmapInfo->BitCount / 8;

  for (YIndex = BitmapInfo->Height; YIndex > 0; YIndex--) {
    for (XIndex = 0; XIndex < BitmapInfo->Width; XIndex++) {
      Pos    = (YIndex - 1) * ALIGN_VALUE (BitmapInfo->Width * BytePerPix, 4) + XIndex * BytePerPix;
      if (BitmapInfo->Invert) {
        BltPos = (YIndex - 1) * BitmapInfo->Width + XIndex;
      } else {
        BltPos = (BitmapInfo->Height - YIndex) * BitmapInfo->Width + XIndex;
      }

      BltBuffer[BltPos].Red      = BitmapData[Pos + 2];
      BltBuffer[BltPos].Green    = BitmapData[Pos + 1];
      BltBuffer[BltPos].Blue     = BitmapData[Pos + 0];
      BltBuffer[BltPos].Reserved = 0;
    }
  }

  return EFI_SUCCESS;
}


試しにNt32Pkg上でEclipseのキャプチャ画像を表示させてみるとこんな感じ。



以上、かなりざっくりと作ったので読みにくい箇所はあると思います。
ざっくり作っても動くものが作れる程度に単純な仕様で素晴らしい、と捉えるべきでしょうか。

2013年5月3日金曜日

[EDK]GraphicsOutputProtocolでBMPを表示させて遊んでみた

UEFIでなにかやってみたかったので、Graphics Output Protocolで画面にBMP画像を表示させてみました。
悲しいことにVMWarePlayerではGraphics Output Protocolに実装不足があったのでNt32Pkgを使って試してます。

使った画像は/MdeModulePkg/Logo/Logo.bmp。
何か表示出来ればいいという程度で作ったのでBMPの処理関係はいろいろ決め打ちです。

infファイル


## @file
#  This is the shell application
#
#  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.

#
#  This program and the accompanying materials
#  are licensed and made available under the terms and conditions of the BSD License
#  which accompanies this distribution. The full text of the license may be found at
#  http://opensource.org/licenses/bsd-license.php
#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#
##

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = TestApp
  FILE_GUID                      = 305671d4-c671-4792-8ada-e424a87f4632
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

#
# The following information is for reference only and not required by the build tools.
#
#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC
#

[Sources]
  Main.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  
[Guids]
  gEfiFileInfoGuid
  
[Protocols]
  gEfiGraphicsOutputProtocolGuid
  gEfiSimpleFileSystemProtocolGuid

と、cソースファイル

#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>

#include <Guid/FileInfo.h>

#include <Protocol/DevicePath.h>
#include <Protocol/GraphicsOutput.h>
#include <Protocol/SimpleFileSystem.h>

#define  MAX_BUFFER_SIZE  SIZE_1MB

EFI_SYSTEM_TABLE   *gST;
EFI_BOOT_SERVICES  *gBS;


#pragma pack(1)
typedef struct {
  CHAR8   Type[2];
  UINT32  Size;
  UINT32  Reserved;
  UINT32  Offset;
  UINT32  CoreHeaderSize;
  UINT32  Width;
  UINT32  Height;
  UINT16  Planes;
  UINT16  BitCount;
} BITMAP_FILE_HEADER;
#pragma pack()

STATIC
EFI_STATUS
LoadBitmapFile (
  IN     CHAR16  *Path,
     OUT VOID    **BmpBuffer,
     OUT UINTN   *BmpSize
  );

STATIC
EFI_STATUS
DrawBmp (
  IN     EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput,
  IN     VOID                          *BmpBuffer,
  IN     UINTN                         BmpSize
  );


EFI_STATUS
EFIAPI
UefiMain (
  IN     EFI_HANDLE        ImageHandle,
  IN     EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                    Status = EFI_SUCCESS;
  EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;
  VOID                          *BmpBuffer = NULL;
  UINTN                         BmpSize;

  gST = SystemTable;
  gBS = gST->BootServices;

  gBS->LocateProtocol (
         &gEfiGraphicsOutputProtocolGuid,
         NULL,
         &GraphicsOutput
       );

  Status = LoadBitmapFile (L"Logo.bmp", &BmpBuffer, &BmpSize);
  if (EFI_ERROR (Status)) {
    if (BmpBuffer != NULL) {
      FreePool (BmpBuffer);
      return Status;
    }
  }

  Status = DrawBmp (GraphicsOutput, BmpBuffer, BmpSize);

  if (BmpBuffer != NULL) {
    FreePool (BmpBuffer);
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
LoadBitmapFile (
  IN     CHAR16  *Path,
     OUT VOID    **BmpBuffer,
     OUT UINTN   *BmpSize
)
{
  EFI_STATUS                       Status = EFI_SUCCESS;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *SimpleFile;
  EFI_FILE_PROTOCOL                *Root;
  EFI_FILE_PROTOCOL                *File;
  UINTN                            BufferSize;
  VOID                             *Buffer = NULL;

  Status = gBS->LocateProtocol (
                  &gEfiSimpleFileSystemProtocolGuid,
                  NULL,
                  &SimpleFile
                );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%r on Locate EFI Simple File System Protocol.\n", Status));
    return Status;
  }

  Status = SimpleFile->OpenVolume (SimpleFile, &Root);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%r on Open volume.\n", Status));
    return Status;
  }

  Status = Root->Open (Root, &File, Path, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%r on Open file.\n", Status));
    return Status;
  }

  BufferSize = MAX_BUFFER_SIZE;
  Buffer = AllocatePool (BufferSize);
  if (Buffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  Status = File->Read (
                   File,
                   &BufferSize,
                   Buffer
                 );
  if (BufferSize == MAX_BUFFER_SIZE) {
    DEBUG ((EFI_D_ERROR, "Buffer Size Too Small.\n"));
    if (Buffer != NULL) {
      FreePool (Buffer);
    }
    return EFI_OUT_OF_RESOURCES;
  }

  Buffer = ReallocatePool (BufferSize, MAX_BUFFER_SIZE, Buffer);

  *BmpBuffer = Buffer;
  *BmpSize   = BufferSize;

  return EFI_SUCCESS;
}


STATIC
EFI_STATUS
DrawBmp (
  IN     EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput,
  IN     VOID                          *BmpBuffer,
  IN     UINTN                         BmpSize
)
{
  EFI_STATUS                     Status = EFI_SUCCESS;
  BITMAP_FILE_HEADER             *BitmapHeader;
  UINT8                          *BitmapData;
  UINT32                         *Palette;
  UINTN                          Pixels;
  UINTN                          XIndex;
  UINTN                          YIndex;
  UINTN                          Pos;
  UINTN                          BltPos;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BltBuffer;

  BitmapHeader = (BITMAP_FILE_HEADER *) BmpBuffer;

  DEBUG ((EFI_D_ERROR, "%d x %d\n", BitmapHeader->Width, BitmapHeader->Height));

  if (BitmapHeader->CoreHeaderSize != 40) {
    return EFI_UNSUPPORTED;
  }
  if (BitmapHeader->BitCount != 8) {
    return EFI_UNSUPPORTED;
  }

  BitmapData = (UINT8*)BmpBuffer + BitmapHeader->Offset;
  Palette    = (UINT32*) ((UINT8*)BmpBuffer + 0x36);/*決め打ち*/

  Pixels = BitmapHeader->Width * BitmapHeader->Height;
  BltBuffer = AllocateZeroPool (
                sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * Pixels
              );
  if (BltBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  for (YIndex = BitmapHeader->Height; YIndex > 0; YIndex--) {
    for (XIndex = 0; XIndex < BitmapHeader->Width; XIndex++) {
      Pos    = (YIndex - 1) * ((BitmapHeader->Width + 3) / 4) * 4 + XIndex;
      BltPos = (BitmapHeader->Height - YIndex) * BitmapHeader->Width + XIndex;
      BltBuffer[BltPos].Blue     = (UINT8) BitFieldRead32 (Palette[BitmapData[Pos]], 0 , 7 );
      BltBuffer[BltPos].Green    = (UINT8) BitFieldRead32 (Palette[BitmapData[Pos]], 8 , 15);
      BltBuffer[BltPos].Red      = (UINT8) BitFieldRead32 (Palette[BitmapData[Pos]], 16, 23);
      BltBuffer[BltPos].Reserved = (UINT8) BitFieldRead32 (Palette[BitmapData[Pos]], 24, 31);
    }
  }

  Status = GraphicsOutput->Blt (
                    GraphicsOutput,
                    BltBuffer,
                    EfiBltBufferToVideo,
                    0                  , 0                   , /*Source X, Y*/
                    200                , 200                 , /*Dest X, Y*/
                    BitmapHeader->Width, BitmapHeader->Height, /*Width, Height*/
                    0
                  );

  FreePool (BltBuffer);

  return EFI_SUCCESS;
}


実行結果はこんな感じです。


地味ではありますが、画像ファイルが表示されました。

今回はBitmapファイルのフォーマットに触れることが出来、色々と悩まされつつも楽しかったです。
標準でBMPファイルを扱えるプロトコルがあればよかったのですが、軽く調べた中では見つからず、結果として普段触らない領域に触れることが出来ました。


参考:
UEFI仕様
Bitmapフォーマット情報

2013年4月23日火曜日

[EDK]VMWarePlayerのUEFI ShellでShellアプリを動かしてみる

VMWarePlayerがEFIに対応しているという情報を見かけたので試してみました。
ついでに、EFI Shellを立ちあげてShellアプリを動かしてみています。

■VMWarePlayerでEFIを有効にする方法
仮想マシンの.vmxファイルに「firmware = "efi"」を追加することでEFIが起動出来るようです。


■Shellアプリの用意
Windows7 64bit上で、Visual Studio2010 Express+Windows SDK 7.1を使ってShellアプリを作成。EFI Shellで動かしてみます。

○EDKの用意
EFIの開発キットを用意
tianocoreから、左ペインのUDK2010→UDK2010.SR1.UP1 Downloadを選択してEDKをダウンロード
・UDK2010.SR1.UP1.Complete.MyWorkSpace.zipを解凍
・更にUDK2010.SR1.UP1.MyWorkSpace.zipとBaseTools(Windows).zipを解凍します
・UDK2010.SR1.UP1.MyWorkSpace\UDK2010内にBaseTools(Windows)の中身をコピー
・UDK2010内のedksetup.batを実行するとConfフォルダが出来上がるので、target.txtを開きます
・「TOOL_CHAIN_TAG=MYTOOLS」を「TOOL_CHAIN_TAG=VS2010x86」に変更

○Shellアプリの作成
ShellPkg\Application\ShellCTestApp\に手頃なコードがあったので、これを使ってShellアプリを作ってみます。
・ShellPkg\ShellPkg.dscを開く
・ファイル末尾に「ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf」を追加
・Visual Studioコマンドプロンプトを起動し、edksetup.batのあるフォルダへcd
・edksetup.batを実行
・「build -p ShellPkg/ShellPkg.dsc -a X64」を実行。今回の環境だと-a X64を指定して64bitアプリにしないと動きませんでした

うまくいくと\Build\Shell\DEBUG_VS2010x86\X64にShellCTestApp.efiというファイルが出来上がります。
こいつをFAT32でフォーマットしたUSBメモリか何かに入れてVMWarePlayerでマウント。


■Shellアプリの起動
Shellアプリを入れたストレージをマウントして仮想マシンを立ちあげ、アプリを実行してみました。

・仮想マシンを起動して暫く待つとEFI Shellが起動します
・Device mapping tableにfs0とかが表示されているかと思います
・「Shell> fs0:」→「fs0:\> ShellCTestApp.efi HelloWorld.」を実行


とっても地味な感じですがとりあえず起動しました。
これを突き詰めていけば、ネットで挙がっているようなグラフィカルな画面とかが作れるのでしょうかね。

2013年3月29日金曜日

[Python]エンコード状況を表示するためのお試しスクリプト

tsファイルのエンコードスクリプトで作成したエンコードスクリプトの進行状況を表示するためのPythonスクリプトを試しに作ってみました。


# -*- coding: utf-8 -*-

import json
import os
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
import time

TASK_LIST_PATH = u'/home/digit/tasklist.json' #Endodeスクリプトで指定したjsonファイルのパス

# Progress bar styles
PENDING_PASS_STYLE = """
QProgressBar {
    text-align: center;
    height: 15px;
}
QProgressBar::chunk {
    width: 10px;
}
"""

FIRST_PASS_STYLE = """
QProgressBar {
    text-align: center;
    height: 15px;
}
QProgressBar::chunk {
    background-color: orange;
    width: 10px;
    margin: 1px;
}
"""

SECOND_PASS_STYLE = """
QProgressBar {
    text-align: center;
    height: 15px;
}
QProgressBar::chunk {
    background-color: lime;
    width: 10px;
    margin: 1px;
}
"""

class TaskList():
    def __init__(self):
        self.path = TASK_LIST_PATH
        self.__load(self.path)
    
    def __load(self, src):
        try:
            f = open(src, 'r')
            self.tasklist = json.loads(f.read())
            f.close
        except:
            print(u'(´・ω・`:∴..')
            self.tasklist = {}

    def update(self):
        self.__load(self.path)
    
    def getwait(self):
        waitlist = []
        for element in self.tasklist.get('wait'):
            element['path'] = os.path.basename(element.get('path', u''));
            waitlist.append(element)
        return waitlist
    
    def getprocess(self):
        processlist = []
        for element in self.tasklist.get('process'):
            element['path'] = os.path.basename(element.get('path', u''));
            processlist.append(element)
        return processlist
    
    def getcompleted(self):
        complist = []
        for element in self.tasklist.get('completed'):
            element['path'] = os.path.basename(element.get('path', u''));
            complist.append(element)
        return complist
    

class CompletedTreeView(QtGui.QTreeView):
    def __init__(self):
        super(CompletedTreeView, self).__init__()
        self._datamodel = QtGui.QStandardItemModel(0, 2)
        self._datamodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Title')
        self._datamodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Date')
        self.setModel(self._datamodel)
        self.show()

    def _add_item(self, completed, n):
        titleitem = QtGui.QStandardItem(completed.get('path', u''))
        self._datamodel.setItem(n, 0, titleitem)
        
        dateitem = QtGui.QStandardItem(completed.get('date', u''))
        self._datamodel.setItem(n, 1, dateitem)

    def _delete_item(self):
        self._datamodel.removeRow(0)

    def append(self, completed):
        self._add_item(completed, self._index)
        self._index = self._index + 1

    def clear(self):
        self._datamodel.removeRows(0, self._datamodel.rowCount())
        self._index = 0


class ProcessTreeView(QtGui.QTreeView):
    def __init__(self):
        super(ProcessTreeView, self).__init__()
        self._datamodel = QtGui.QStandardItemModel(0, 2)
        self._datamodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Title')
        self._datamodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Progress')
        self.setModel(self._datamodel)
        self.show()

    def _add_item(self, process, n):
        titleitem = QtGui.QStandardItem(process.get('path', u''))
        self._datamodel.setItem(n, 0, titleitem)
        
        pbar = QtGui.QProgressBar(self)
        pbar.setValue(process.get('progress', 0))
        if process.get('pass', 0) == 1:
            pbar.setStyleSheet(FIRST_PASS_STYLE)
            pbar.setFormat('First Pass:%p%')
            pbar.setRange(0, 100)
        elif process.get('pass', 0) == 2:
            pbar.setStyleSheet(SECOND_PASS_STYLE)
            pbar.setFormat('Second Pass:%p%')
            pbar.setRange(0, 100)
        else:
            pbar.setStyleSheet(PENDING_PASS_STYLE)
            pbar.setFormat('')
            # maquee style
            pbar.setRange(0, 0)
        
        index = self._datamodel.index(n, 1, QtCore.QModelIndex())
        self.setIndexWidget(index, pbar)

    def _delete_item(self):
        self._datamodel.removeRow(0)

    def append(self, process):
        self._add_item(process, self._index)
        self._index = self._index + 1

    def clear(self):
        self._datamodel.removeRows(0, self._datamodel.rowCount())
        self._index = 0
    

class WaitTreeView(QtGui.QTreeView):
    def __init__(self):
        super(WaitTreeView, self).__init__()
        self._datamodel = QtGui.QStandardItemModel(0, 2)
        self._datamodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Title')
        self._datamodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Date')
        self.setModel(self._datamodel)
        self.show()

    def _add_item(self, wait, n):
        titleitem = QtGui.QStandardItem(wait.get('path', u''))
        self._datamodel.setItem(n, 0, titleitem)
        
        dateitem = QtGui.QStandardItem(wait.get('date', u''))
        self._datamodel.setItem(n, 1, dateitem)

    def _delete_item(self):
        self._datamodel.removeRow(0)

    def append(self, wait):
        self._add_item(wait, self._index)
        self._index = self._index + 1

    def clear(self):
        self._datamodel.removeRows(0, self._datamodel.rowCount())
        self._index = 0
    
    
class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        self.tasklist = None;
        super(MainWindow, self).__init__()
        self.initUI()
        
    def initUI(self):
        self.timer = QtCore.QBasicTimer()
        
        self.completed = CompletedTreeView()
        self.completed.setColumnWidth(0, 450)
        self.process   = ProcessTreeView()
        self.process.setColumnWidth(0, 450)
        self.wait      = WaitTreeView()
        self.wait.setColumnWidth(0, 450)
        
        self.update()
        
        splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
        splitter.addWidget(self.completed)
        splitter.addWidget(self.process)
        splitter.addWidget(self.wait)
        
        self.setCentralWidget(splitter)
        
        self.setGeometry(300, 300, 600, 400)
        self.setWindowTitle('Endoding process viewer')
        self.show()
        
        self.timer.start(60000, self)
    
    def setTasklist(self, tasklist):
        self.tasklist = tasklist
    
    def timerEvent(self, event):
        if event.timerId() == self.timer.timerId():
            self.update()
        else:
            QtGui.QFrame.timerEvent(self, event)
    
    def update(self):
        if self.tasklist is None:
            return
        self.tasklist.update()
        self.dispCompleted(self.tasklist)
        self.dispProcess(self.tasklist)
        self.dispWait(self.tasklist)
    
    def dispCompleted(self, tasklist):
        self.completed.clear()
        for element in tasklist.getcompleted():
            self.completed.append(element)
    
    def dispProcess(self, tasklist):
        self.process.clear()
        for element in tasklist.getprocess():
            self.process.append(element)

    def dispWait(self, tasklist):
        self.wait.clear()
        for element in tasklist.getwait():
            self.wait.append(element)

class WindowTypeQt():
    def __init__(self):
        self.app = QtGui.QApplication(sys.argv)
        self.mainwindow = MainWindow()

    def show(self, tasklist):
        self.mainwindow.setTasklist(tasklist)
        self.mainwindow.update()
        sys.exit(self.app.exec_())
        
def main():
    gui = WindowTypeQt()
    tasklist = TaskList()
    
    gui.show(tasklist)

if __name__ == '__main__':
    main()


実行してみるとこんな感じです。


それぞれ以下を表示するようにしました。
・上段にエンコード済みのファイル
・中断にエンコード中のファイル
・下段にエンコード待ちのファイル

tasklist.jsonをネットワークフォルダに置けば、サーバ端末でのエンコード状況をクライアント端末から見ることが出来ます。

正直お試しなので、エンコードスクリプト、表示スクリプト共に課題は山盛りですが、私が使用する分にはそこまで困らないくらいのレベルで使えています。
改善点は少しずつ直していこうかと思っています。

2013年3月18日月曜日

[PyQt]QProgressBarで遊んでみた

PtQtのプログレスバーで少し遊んでみました。


■表示する文字を変更する
QProgressBarに表示する文字を変更するには、sefFormatを使います。
以下の文字はそれぞれ置換されるようです。

  • %p…進捗率
  • %v…完了した数
  • %m…全体の数



■表示スタイルの変更
QProgressBarの見た目を変更するには、setStyleSheetを利用します。
外枠の部分を変更する場合は、QProgressBarを、内側のバーを変更する場合はQProgressBar::chunkを設定するようです。
指定できるスタイルの種類は調べきれていません。

参考:Customizing QProgressBar

■ビジー状態の表示
ビジー状態などで、進捗率は表示できないけれどバーは動かしたい場合は、QProgressBarのMaximumとMinimumを共に設定すれば良いようです。
Maquee styleと言うみたいですね。

参考:http://qt-project.org/forums/viewthread/2350


試しに動かしてみました。

2013年3月16日土曜日

[PyQt]QTreeView内にWidgetを配置

PyQtのQTreeViewの中にProgressBarを配置したかったのでやり方を調べてみました。
どうやら、QAbstractItemView::setIndexWidgetを使ってIndexにWidgetをセットしてあげれば良いようです。

# -*- coding: utf-8 -*-

import sys
from PyQt4 import QtCore
from PyQt4 import QtGui

dummydata = [
        {
            "path": "Title A",
            "progress": 7,
            "pass": 2
        }, {
            "path": "Movie B",
            "progress": 18,
            "pass": 2
        }, {
            "path": "Dummy",
            "progress": 35,
            "pass": 2
        }, {
             "path": "HogeHoge",
             "progress": 15,
             "pass": 1
        }
    ]
    
class ProcessTreeView(QtGui.QTreeView):
    def __init__(self):
        super(ProcessTreeView, self).__init__()
        
        self._datamodel = QtGui.QStandardItemModel(0, 3)
        self._datamodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Title')
        self._datamodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Pass')
        self._datamodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Process')
        self.setModel(self._datamodel)

        index = 0
        for element in dummydata:
            self._add_item(element, index)
            index = index + 1

        self.show()

    def _add_item(self, process, n):
        titleitem = QtGui.QStandardItem(process.get('path', u''))
        self._datamodel.setItem(n, 0, titleitem)
        passitem = QtGui.QStandardItem(str(process.get('pass', 0)))
        self._datamodel.setItem(n, 1, passitem)
        
        pbar = QtGui.QProgressBar(self)
        pbar.setValue(process.get('progress', 0))
        index = self._datamodel.index(n, 2, QtCore.QModelIndex())
        self.setIndexWidget(index, pbar)
        

class Example(QtGui.QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.initUI()
        
    def initUI(self):
        self.treeview = ProcessTreeView()
        
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(self.treeview)
        
        self.setLayout(hbox)
        
        self.setWindowTitle('Tree view test')
        self.show()

def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

実行結果




        index = self._datamodel.index(n, 2, QtCore.QModelIndex())
で、Widgetの配置先のindexを取得し、

        self.setIndexWidget(index, pbar)
setIndexWidgetを使用して取得したindexにWidgetを配置しています。


参考:
http://qt-project.org/doc/qt-4.8/qabstractitemview.html

2013年3月11日月曜日

[Python]tsファイルのエンコードスクリプト

溜まったTSをいちいちエンコードするのも面倒なので、pythonの練習も兼ねてスクリプトを作成してみました。
例外処理とか色々荒削りな部分もありますが、とりあえず動けばいいやレベルで。

以下、スクリプト全体。


2013年3月10日日曜日

SyntaxHighlighter投稿テスト

Bloggerの投稿テスト+SyntaxHighlighterの投稿テスト。

# -*- coding: utf-8 -*-

if __name__ == '__main__':
    print ("Hello world.")