DataGrid: CellTemplate
ListBox/ComboBox/TreeView: ItemTemplate
UserControl: ContentTemplate

listbox

<Window.Resources>
<DataTemplate x:Key="comTemplate">
<StackPanel Orientation="Horizontal" Margin="5,0">
<Border Width="10" Height="10" Background="{Binding Code}"/>
<TextBlock Text="{Binding Code}" Margin="5,0"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ComboBox Name="cob" Width="120" Height="30" ItemTemplate="{StaticResource comTemplate}"/>
<ListBox Name="lib" Width="120" Height="100" Margin="5,0" ItemTemplate="{StaticResource comTemplate}"/>
</StackPanel>
</Grid>


List<Color> ColorList = new List<Color>();
ColorList.Add(new Color() { Code = "#FF8C00" });
ColorList.Add(new Color() { Code = "#FF7F50" });
ColorList.Add(new Color() { Code = "#FF6EB4" });
ColorList.Add(new Color() { Code = "#FF4500" });
ColorList.Add(new Color() { Code = "#FF3030" });
ColorList.Add(new Color() { Code = "#CD5B45" });

cob.ItemsSource = ColorList;
lib.ItemsSource = ColorList;

在资源里面定义 style 可以在控件中引用。

如果控件中有指定的样式,则覆盖资源定义的 style

资源样式支持继承。

 <Window.Resources>
<Style x:Key="btnStyleBase" TargetType="Button">
<Setter Property="FontSize" Value="30"></Setter>
</Style>
<Style x:Key="btnStyle" TargetType="Button" BasedOn="{StaticResource btnStyleBase}">
<Setter Property="Foreground" Value="Blue"></Setter>
</Style>
</Window.Resources>
<Grid x:Name="grid_main">
<Button Style="{StaticResource btnStyle}" Grid.Row="1" Click="Button_Click">
<Button.FontWeight>Bold</Button.FontWeight>
...
</Button>
...
</Grid>

触发器

可以通过触发器动态改变样式

<Style x:Key="btnStyleBase" TargetType="Button">
<Setter Property="FontSize" Value="30"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"></Setter>
<Setter Property="FontSize" Value="10"></Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Foreground" Value="Blue"></Setter>
<Setter Property="FontSize" Value="30"></Setter>
</Trigger>
</Style.Triggers>
</Style>

多条件触发

<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"></Condition>
<Condition Property="IsFocused" Value="True"></Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Foreground" Value="Red"></Setter>
</MultiTrigger.Setters>
</MultiTrigger>

事件触发器

<Window.Resources>
<Style x:Key="btnStyleBase" TargetType="Button">
<Setter Property="FontSize" Value="30"></Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1"
Storyboard.TargetProperty="FontSize"
To="10" ></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
<Style x:Key="btnStyle" TargetType="Button" BasedOn="{StaticResource btnStyleBase}">
<Setter Property="Foreground" Value="Blue"></Setter>
</Style>
</Window.Resources>

TreeViewItem

绑定 TreeView 到数据结构,使用适当的模板渲染内容。

xaml

<TreeView Name="treeView" Padding="0" Margin="5 5 0 5"  Loaded="treeView_Loaded">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeNodeItem}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{Binding name}"></TextBlock>
<StackPanel.ToolTip>
<TextBlock VerticalAlignment="Center" Text="{Binding tips}" TextWrapping="Wrap" MaxWidth="200" ></TextBlock>
</StackPanel.ToolTip>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

展开、收缩

需要在控件的 Loaded 事件里面处理

private void ExpandTree()
{
if (this.treeView.Items != null && this.treeView.Items.Count > 0)
{
foreach (var item in this.treeView.Items)
{
DependencyObject dependencyObject = this.treeView.ItemContainerGenerator.ContainerFromItem(item);
if (dependencyObject != null)//第一次打开程序,dependencyObject为null,会出错
{
((TreeViewItem)dependencyObject).ExpandSubtree();
}
}
}
}

private void treeView_Loaded(object sender, RoutedEventArgs e)
{
ExpandTree();
}

事件

在 xaml 里面添加事件,对应的 cs 文件里面会添加映射函数

<TreeView Name="treeView" Padding="0" Margin="5 5 0 5"  Loaded="treeView_Loaded" SelectedItemChanged="treeView_SelectedItemChanged">
...
</TreeView>

选择某一项

设置需要选择的项目的 IsSelected 为 true 就可以了

// xaml
<TreeView Name="treeView" Padding="0" Margin="5 5 0 5" Loaded="treeView_Loaded" SelectedItemChanged="treeView_SelectedItemChanged">
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeNodeItem}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{Binding name}"></TextBlock>
<StackPanel.ToolTip>
<TextBlock VerticalAlignment="Center" Text="{Binding tips}" TextWrapping="Wrap" MaxWidth="200" ></TextBlock>
</StackPanel.ToolTip>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

// code
internal class TreeNodeItem
{
public string name { get; set; }
public string tips { get; set; }
public int data { get; set; }
public bool IsSelected { get; set; }

public List<TreeNodeItem> Children { get; set; }
public TreeNodeItem()
{
Children = new List<TreeNodeItem>();
}
public TreeNodeItem(string inName, string inTips,int inData,bool inIsSelected=false)
{
name = inName;
tips = inTips;
data = inData;
IsSelected = inIsSelected;
Children = new List<TreeNodeItem>();
}
}

失去焦点时设置背景色

<TreeView Name="treeView" Padding="0" Margin="5 5 0 5"  Loaded="treeView_Loaded" 
SelectedItemChanged="treeView_SelectedItemChanged">
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
<!-- Style the inactive selection the same as active -->
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}"
Color="{DynamicResource {x:Static SystemColors.HighlightColorKey}}" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}"
Color="{DynamicResource {x:Static SystemColors.HighlightTextColorKey}}"/>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeNodeItem}" ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{Binding name}"></TextBlock>
<StackPanel.ToolTip>
<TextBlock VerticalAlignment="Center" Text="{Binding tips}" TextWrapping="Wrap" MaxWidth="200" ></TextBlock>
</StackPanel.ToolTip>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

grid

When you define a column in a WPF grid you can set the width to one of three possible values:

  • A fixed width,
  • Auto – column will become as wide as necessary to fit its children, or
    • (star) take up any available remaining space

The * is prefixed by a number (default is 1 if no number is specified). The available space is divided among the starred columns in proportion to the prefix number.

<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.07*"/>
<ColumnDefinition Width="0.93*"/>
</Grid.ColumnDefinitions>
阅读全文 »

注意加载数据需要在 Loaded 事件里面。因为表格还没加载完毕时是无法显示数据的。

更新完 dg.ItemsSource 对应的 List 之后,表格会自动更新,增加和删除都可以

自动生成

根据数据源字段自动生成列

<Grid Margin="10">
<DataGrid Name="dgSimple"></DataGrid>
</Grid>

List<User> users = new List<User>();
users.Add(new User() { dbId=11, Id = 1, Name = "John Doe", Birthday = new DateTime(1971, 7, 23) });
users.Add(new User() { dbId = 12, Id = 2, Name = "Jane Doe", Birthday = new DateTime(1974, 1, 17) });
users.Add(new User() { dbId = 13, Id = 3, Name = "Sammy Doe", Birthday = new DateTime(1991, 9, 2) });

dgSimple.ItemsSource = users;

public class User
{
public int dbId { get; set; }
public int Id { get; set; }

public string Name { get; set; }

public DateTime Birthday { get; set; }
}
阅读全文 »

通过 共享内存 和其他进程通信

try:
log.info('fireworks_plugin start')
shm = shared_memory.SharedMemory(create=False, size=mem_share_size, name='fireworksShareMemory')
with Pair0(dial=address) as s1:
s1.send(b'python login')
while True:
msg = s1.recv().decode(encoding='utf-8')
print('python:' + msg)
log.info('receive: ' + msg)
if msg == 'exit':
s1.send(b'python bye')
time.sleep(0.5)
log.info('exit nng loop')
break
elif msg.startswith('datas') or msg.startswith('ready'):
log.info('receive ' + msg)
# datas,12332 后面是共享内存数据大小
# load data process by memory mapping mmap file
# 收到的数据格式:"datas,size,cur index, all count"
param_list = msg.split(',')
if len(param_list) >= 4:
# 获取数据大小
data_size = int(param_list[1])
# 读取共享内存数据放到 share_df_list 里面
load_share_data(shm, data_size)
# 最后一组数据,接收完毕后可以处理
if param_list[2] == param_list[3]:
log.info('get last group data')
ret = proc_raw_data(share_df_list)
print('Python datas_RESULT,' + ''.join(ret))
log.info('datas_RESULT,' + ''.join(ret))
s1.send(bytes('datas_RESULT,' + ''.join(ret), encoding='gbk'))
else:
s1.send(b'datas_OK')
pass

# proc_raw_data()
shm.close()
shm.unlink() # Free and release the shared memory block at the very end
log.info('exit plugin')
except Exception as e:
import traceback
traceback.print_exc()
errMsg = traceback.format_exc()
print("Error: " + errMsg)
log.error("call plugin exception." + errMsg)
except:
import traceback

traceback.print_exc()
errMsg = traceback.format_exc()
print("Error: " + errMsg)
log.error("call plugin exception." + errMsg)

安装

pip3 install pynng

使用样例

from pynng import Pair0

with Pair0(dial=address) as s1:
s1.send(b'python login')
while True:
msg = s1.recv().decode(encoding='utf-8')
print('python:' + msg)
log.info('receive: ' + msg)
if msg == 'exit':
s1.send(b'python bye')
time.sleep(0.5)
log.info('exit nng loop')
break
elif msg.startswith('datas') or msg.startswith('ready'):
log.info('receive ' + msg)
# datas,12332 后面是共享内存数据大小
# load data process by memory mapping mmap file
# 收到的数据格式:"datas,size,cur index, all count"
param_list = msg.split(',')
if len(param_list) >= 4:
# 获取数据大小
data_size = int(param_list[1])
# 读取共享内存数据放到 share_df_list 里面
load_share_data(shm, data_size)
# 最后一组数据,接收完毕后可以处理
if param_list[2] == param_list[3]:
log.info('get last group data')
ret = proc_raw_data(share_df_list)
print('Python datas_RESULT,' + ''.join(ret))
log.info('datas_RESULT,' + ''.join(ret))
s1.send(bytes('datas_RESULT,' + ''.join(ret), encoding='gbk'))
else:
s1.send(b'datas_OK')
pass

接收数据不全

发送的数据中有中文

设置标题文字

w.setWindowTitle(QObject::tr("remoteCom ver: 2020.06.10"));

设置中心组件

remoteCom::remoteCom(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
this->setCentralWidget(new QTextEdit(this));
}

pyd 相当于 dll。好处多多

编译 pyd

装了 nuitka 之后,这个简单,这个在使用 winpython 的时候,调用失败了。使用下面的 cython 就可以。

nuitka --mingw64 --module --show-progress --output-dir=o 你的.py
  1. 安装 cython
pip install Cython
  1. 新建 setup.py

根据实际情况修改内容,下面的 language 是支持 python3

from distutils.core import setup
from Cython.Build import cythonize

setup(
name = 'arc app',
ext_modules = cythonize(["proc_ui.py","proc_data.py"],compiler_directives={'language_level': 3}),
)
  1. 生成 pyd
python setup.py build_ext --inplace

我这边这样就出结果了。 没遇到任何问题,改名为 main.pyd
生成了 main.cp38-win_amd64.pyd 文件

  1. 写个入口脚本

注意 win7 需要 Windows6.1-KB2533623-x64.msu 补丁

查看当前路径

# python 环境下
import sys
sys.path

WinPython

github

pyd 文件搜索路径
set PYTHONPATH = 路径

path 里面添加 dll 路径
tkArcTool\Library\bin

官网下载 python-3.8.3-embed-amd64 版本

注意系统需要先确认一下 3.8.3 安装版能否正常安装,我虚拟机的win7版本过于老旧,缺少补丁包,导致socket dll 加载总是失败。问题找了好久。
我装的 windows 版本 提示缺少 KB2533623 补丁包

修改 python38.pth 内容

添加搜索路径,去掉 import site 前面的注释

python38.zip
.
.\Lib
.\Lib\site-packages
.\DLLs

# Uncomment to run site.main() automatically
import site

进入目录里面,执行 python,进入终端,以下操作都在该终端内执行

安装 pip

pip site
下载 get-pip.py 网页另存为比较快

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py --no-setuptools --no-wheel

get-pip.py options
--no-setuptools
If set, do not attempt to install setuptools

--no-wheel
If set, do not attempt to install wheel

安装其他需要的包

Scripts\pip install pandas  -i https://pypi.tuna.tsinghua.edu.cn/simple 
Scripts\pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple

然后拷贝到目标机就可以使用了。

移除 pip

python -m pip uninstall pip setuptools wheel

添加 tkinter

从虚拟环境中复制下面文件夹和文件

tcl
Lib/tkinter
DLLs/_tkinter.pyd, tcl86t.dll, tk86t.dll

主要问题就是设置搜索路径,让 python 找到库和 dll

set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0;./Library/bin;./Dlls;./tcl;./Scripts;./libs;./Lib;

ModuleNotFoundError: No module named ‘Tkinter’

tcl folder to embedded_distribution_folder\ (root folder of the embedded distribution)
tkinter folder (which is under Lib) either to embedded_distribution_folder\Lib or to embedded_distribution_folder\
files _tkinter.pyd tcl86t.dll tk86t.dll (which are under DLLs) either to embedded_distribution_folder\DLLs or to embedded_distribution_folder\