2013
03
29
03
29
IronPython: WPFでフォトビューワを作ってみる
IronPythonでWPFを使って、フォトビューワを作ってみる。写真の表示切り換えは瞬間的に行われるのではなく、古い写真がフェードアウトしてから新しい写真がフェードインするようにしたい。そういう場合に使うのがStotyboardとDoubleAnimation。
このアニメーション効果は、XAMLに表記することで簡単に使える。これは各Control要素のイベントに結びつけやすい。下記のXAMLはマウスのオン、オフによっておこるアニメーションを記述してある。
一方で、スクリプト内で書くこともできる。こうしておくとスクリプトから呼び出しやすい。
ターゲットのフェードアウト完了から、フェードインを呼びたかったので、Completedイベントにメソッドへの参照を渡している。SomeMethod()に写真の入れ替えを記述しておけば、フェードアウト中に写真の入れ替えを行ってくれる。
下記のスクリプトの実行にはMicrosoft.Windows.Shell.dllが必要。
http://msdn.microsoft.com/ja-jp/library/ff799534(v=vs.100).aspx
Window1.py
Window1.xaml
Application.py
このアニメーション効果は、XAMLに表記することで簡単に使える。これは各Control要素のイベントに結びつけやすい。下記のXAMLはマウスのオン、オフによっておこるアニメーションを記述してある。
<DockPanel.Triggers>
<!-- Animates the rectangle's opacity. -->
<EventTrigger RoutedEvent="DockPanel.MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="control"
Storyboard.TargetProperty="Opacity"
To="0.0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="DockPanel.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="control"
Storyboard.TargetProperty="Opacity"
To="1.0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DockPanel.Triggers>
一方で、スクリプト内で書くこともできる。こうしておくとスクリプトから呼び出しやすい。
ターゲットのフェードアウト完了から、フェードインを呼びたかったので、Completedイベントにメソッドへの参照を渡している。SomeMethod()に写真の入れ替えを記述しておけば、フェードアウト中に写真の入れ替えを行ってくれる。
self.fadeOut = DoubleAnimation()
self.fadeOut.To = 0.0
self.fadeOut.Duration = Duration(TimeSpan.FromSeconds(0.3))
self.fadeoutStory = Storyboard()
self.fadeoutStory.Children.Add(self.fadeOut)
self.fadeoutStory.SetTargetName(self.fadeOut, self.background.Name)
self.fadeoutStory.SetTargetProperty(self.fadeOut, PropertyPath(self.background.OpacityProperty))
self.fadeIn = DoubleAnimation()
self.fadeIn.To = 1.0
self.fadeIn.Duration = Duration(TimeSpan.FromSeconds(0.3))
self.fadeinStory = Storyboard()
self.fadeinStory.Children.Add(self.fadeIn)
self.fadeinStory.SetTargetName(self.fadeIn, self.background.Name)
self.fadeinStory.SetTargetProperty(self.fadeIn, PropertyPath(self.background.OpacityProperty))
self.fadeoutStory.Completed += self.FadeoutCompleted
def FadeoutCompleted(self, target, e):
SomeMethod()
self.fadeinStory.Begin(self.background)
下記のスクリプトの実行にはMicrosoft.Windows.Shell.dllが必要。
http://msdn.microsoft.com/ja-jp/library/ff799534(v=vs.100).aspx
Window1.py
import sys, clr, wpf
clr.AddReference('System.Windows.Forms')
clr.AddReference('System.Drawing')
clr.AddReference('Microsoft.Windows.Shell')
import System
from System import Windows, Threading, Uri, TimeSpan, Environment
from System.Windows import Window, Controls, Thickness, Duration, PropertyPath
from System.Windows.Media import Imaging, ImageBrush, Brushes
from System.Windows.Media.Animation import DoubleAnimation, Timeline, Storyboard
from System import Action
from System.IO import Directory, DirectoryInfo, Path
import System.Windows.Forms
import System.Drawing
EXT = ["*.jpg", "*.jpeg", "*.png", "*.gif",]
class Window1 (Window):
def __init__(self):
wpf.LoadComponent(self, 'Window1.xaml')
iconUri = System.Uri("icon.ico", System.UriKind.RelativeOrAbsolute)
self.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconUri)
self.myGrid = self.FindName('myGrid')
self.windowBorder = self.FindName('windowBorder')
self.ApplyTemplate() #important to get VisualBrush in ControlTemplate
self.imageBrush = self.Template.FindName("imageBrush", self)
self.background = self.Template.FindName('background', self)
self.control = self.FindName('control')
self.playButton = self.FindName('playButton')
self.Show()
self.GetPictures()
self.Height = 600
self.fadeOut = DoubleAnimation()
self.fadeOut.To = 0.0
self.fadeOut.Duration = Duration(TimeSpan.FromSeconds(0.3))
self.fadeoutStory = Storyboard()
self.fadeoutStory.Children.Add(self.fadeOut)
self.fadeoutStory.SetTargetName(self.fadeOut, self.background.Name)
self.fadeoutStory.SetTargetProperty(self.fadeOut, PropertyPath(self.background.OpacityProperty))
self.fadeIn = DoubleAnimation()
self.fadeIn.To = 1.0
self.fadeIn.Duration = Duration(TimeSpan.FromSeconds(0.3))
self.fadeinStory = Storyboard()
self.fadeinStory.Children.Add(self.fadeIn)
self.fadeinStory.SetTargetName(self.fadeIn, self.background.Name)
self.fadeinStory.SetTargetProperty(self.fadeIn, PropertyPath(self.background.OpacityProperty))
self.fadeoutStory.Completed += self.FadeoutCompleted
def FadeoutCompleted(self, target, e):
self.imageBrush.ImageSource = Imaging.BitmapImage(Uri(self.filename))
self.fadeinStory.Begin(self.background)
def GetPictures(self):
#path = Path.GetDirectoryName(filename)
path = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
pics = []
for ext in EXT:
pics += DirectoryInfo(path).GetFiles(ext, System.IO.SearchOption.AllDirectories)
self.pics = [pic.FullName for pic in pics]
self.imageBrush.ImageSource = Imaging.BitmapImage(Uri(self.pics[0]))
self.filename = self.pics[0]
def forward(self, target, e):
self.fadeoutStory.Begin(self.background)
pointer = self.pics.index(self.filename)
if pointer < len(self.pics) - 1:
self.filename = self.pics[pointer + 1]
else:
self.filename = self.pics[0]
def rewind(self, target, e):
self.fadeoutStory.Begin(self.background)
pointer = self.pics.index(self.filename)
if pointer != 0:
self.filename = self.pics[pointer - 1]
else:
self.filename = self.pics[len(self.pics) - 1]
def close_window(self, target, e):
self.Close()
def state_change(self, target, e):
state = self.WindowState
if state == System.Windows.WindowState.Normal:
self.WindowState = System.Windows.WindowState.Maximized
elif state == System.Windows.WindowState.Maximized:
self.WindowState = System.Windows.WindowState.Normal
def minimize_window(self, target, e):
self.WindowState = System.Windows.WindowState.Minimized
def move(self, target, e):
self.DragMove()
def switch_fit(self, target, e):
state = self.imageBrush.Stretch
if state == System.Windows.Media.Stretch.Uniform:
self.imageBrush.Stretch = System.Windows.Media.Stretch.UniformToFill
else:
self.imageBrush.Stretch = System.Windows.Media.Stretch.Uniform
def top_mode(self, target, e):
state = self.Topmost
print state
if state == True:
self.Topmost = False
else:
self.Topmost = True
Window1.xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title=""
FontFamily="Meiryo UI"
Width="800"
MinWidth="320"
Height="600"
MinHeight="200"
Name="window"
Background="Aqua"
WindowStartupLocation="CenterScreen"
WindowStyle="SingleBorderWindow"
ResizeMode="CanResizeWithGrip" >
<Window.CommandBindings>
<CommandBinding Command="{x:Static shell:SystemCommands.CloseWindowCommand}"
Executed="close_window"/>
<CommandBinding Command="{x:Static shell:SystemCommands.MaximizeWindowCommand}"
Executed="state_change"/>
<CommandBinding Command="{x:Static shell:SystemCommands.MinimizeWindowCommand}"
Executed="minimize_window"/>
</Window.CommandBindings>
<Window.Resources>
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}" x:Name="buttonStyle1">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Gray" BorderThickness="1" Margin="0,0,0,0" >
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ButtonStyle2" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border"
BorderThickness="1"
Padding="4,2"
BorderBrush="DarkGray"
CornerRadius="3"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Window.Style>
<Style TargetType="Window">
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome GlassFrameThickness="1" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<Grid>
<Border Name="background" BorderThickness="0.8" BorderBrush="Aqua">
<Border.Background>
<ImageBrush
x:Name="imageBrush"
Stretch="Uniform" />
</Border.Background>
<ContentPresenter Margin="0" Content="{TemplateBinding Content}"/>
</Border>
<!-- Title プロパティを表示する -->
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Margin="34,8,0,0" Foreground="White"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}" />
<!-- 最小化、最大化、閉じるボタン -->
<StackPanel Name="frameButtons" shell:WindowChrome.IsHitTestVisibleInChrome="True" Orientation="Horizontal" Margin="0,6,6,0" HorizontalAlignment="Right" VerticalAlignment="Top" Background="Transparent" Opacity="0">
<StackPanel.Triggers>
<!-- Animates the rectangle's opacity. -->
<EventTrigger RoutedEvent="StackPanel.MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="frameButtons"
Storyboard.TargetProperty="Opacity"
To="0.0" Duration="0:0:0.5"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="StackPanel.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="frameButtons"
Storyboard.TargetProperty="Opacity"
To="1.0" Duration="0:0:0.5"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<Button shell:WindowChrome.IsHitTestVisibleInChrome="True"
Style="{StaticResource ButtonStyle2}"
Height="24" Margin="30,10,0,0" Width="28"
Background="Black"
BorderThickness="0"
Click="switch_fit" >
<TextBlock Foreground="White">> <</TextBlock>
</Button>
<Button shell:WindowChrome.IsHitTestVisibleInChrome="True"
Style="{StaticResource ButtonStyle2}"
Height="24" Margin="30,10,0,0" Width="28"
Background="Black"
BorderThickness="0"
Command="{x:Static shell:SystemCommands.MinimizeWindowCommand}"
CommandParameter="{Binding ElementName=window}">
<TextBlock Foreground="White">_</TextBlock>
</Button>
<Button shell:WindowChrome.IsHitTestVisibleInChrome="True"
Style="{StaticResource ButtonStyle2}"
Height="24" Margin="30,10,0,0" Width="28"
Background="Black"
Command="{x:Static shell:SystemCommands.MaximizeWindowCommand}"
CommandParameter="{Binding ElementName=window}">
<TextBlock Foreground="White" FontFamily="Marlett">1</TextBlock>
</Button>
<Button shell:WindowChrome.IsHitTestVisibleInChrome="True"
Style="{StaticResource ButtonStyle2}"
Height="24" Margin="30,10,20,0" Width="28"
Background="Black"
Command="{x:Static shell:SystemCommands.CloseWindowCommand}"
CommandParameter="{Binding ElementName=window}">
<TextBlock Foreground="White" FontFamily="Marlett">r</TextBlock>
</Button>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Style>
<DockPanel
x:Name="myGrid"
Width="Auto"
Height="Auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Transparent"
MouseDown="move">
<DockPanel.Triggers>
<!-- Animates the rectangle's opacity. -->
<EventTrigger RoutedEvent="DockPanel.MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="control"
Storyboard.TargetProperty="Opacity"
To="0.0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="DockPanel.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="control"
Storyboard.TargetProperty="Opacity"
To="1.0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DockPanel.Triggers>
<WrapPanel
Name="control"
Width="Auto"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
DockPanel.Dock="Bottom" >
<Button
HorizontalAlignment="Right"
Width="75"
Height="35"
Click="rewind"
Margin="10,0,10,10" Background="White" FontWeight="Bold" >
<Image Source="rewind.png" />
</Button>
<Button
Name="forwardButton"
HorizontalAlignment="Right"
Width="75"
Height="35"
Margin="10,0,10,10" Background="White" FontWeight="Bold"
Click="forward" >
<Image Source="forward.png" />
</Button>
<ResizeGrip
HorizontalAlignment="Right"
VerticalAlignment="Bottom" />
</WrapPanel>
</DockPanel>
</Window>
Application.py
import wpf, clr
from System.Windows import Application
from Window1 import Window1
window = Window1()
app = Application()
app.Run(window)
スポンサーサイト