Gamer's Show

你知道人生最要紧的事是快乐不停

0%

有一些tag插件在8以下版本是不能用的,还有一些用markdown完全可以轻松代替,这里不作过多赘述,只是列出来一些笨人觉得有用并且很方便的~
以及,本文中的[]只是分割名字,实际用的时候不需要敲出来。

Note

Next配置

1
2
3
4
5
6
7
8
9
10
11
note:
# Note tag style values:
# - simple bs-callout old alert style. Default.
# - modern bs-callout new (v2-v3) alert style.
# - flat flat callout style with background, like on Mozilla or StackOverflow.
# - disabled disable all CSS styles import of note tag.
style: simple
icons: false
# Offset lighter of background in % for modern and flat styles (modern: -12 | 12; flat: -18 | 6).
# Offset also applied to label tag variables. This option can work with disabled note tag.
light_bg_offset: 0

用法

1
2
3
{% note [class] [no-icon] [summary] %}
Any content (support inline tags too).
{% endnote %}

所有的参数都是自选的

  • class:Supported values:default(灰色向右箭头) | primary(紫色加号) | success(绿色对号) | info(蓝色i) | warning(黄色感叹号) | danger(红色减号).
  • [no-icon] : Disable icon in note.
  • [summary] : Optional summary of the note.前边会自带一个小三角
    1
    2
    3
    4
    {% note primary %}
    #### Primary Header
    **Welcome** to [Hexo!](https://hexo.io)
    {% endnote %}

Tabs

Next配置

1
2
3
4
5
6
tabs:
# Make the nav bar of tabs with long content stick to the top.
sticky: false
transition:
tabs: false
labels: true

用法

1
2
3
4
5
{% tabs Unique name, [index] %}
<!-- tab [Tab caption] [@icon] -->
Any content (support inline tags too).
<!-- endtab -->
{% endtabs %}
  • Unique name : Unique name of tabs block tag without comma.
    Will be used in #id’s as prefix for each tab with their index numbers.
    If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.
    Only for current url of post/page must be unique!
    就是说这个参数是给你的表设一个名字,在本篇文章中仅此一个。如果要单独给某个表格设置名字在Tab caption处设置就可以。
  • [index] : Index number of active tab.
    If not specified, first tab (1) will be selected.
    If index is -1, no tab will be selected. It’s will be something like spoiler.
    选择初始时显示的表,默认是1,-1是全收缩
    Optional parameter.
  • [Tab caption] : Caption of current tab.
    If not caption specified, unique name with tab index suffix will be used as caption of tab.
    If not caption specified, but specified icon, caption will empty.
    给表头单独添加索引,默认是从1编号;-1是不制表;如果是空的,就是name+id
    Optional parameter.
  • [@icon] : Font Awesome icon name.
    Can be specified with or without space; e.g. ‘Tab caption @icon’ is the same as ‘Tab caption@icon’.
    @+font的名字,和name之间有没有空格都可以
    Optional parameter

Mermaid

设置

hexo设置

1
2
3
highlight:
exclude_languages:
- mermaid

Next设置

1
2
3
4
5
6
7
# Mermaid tag
mermaid:
enable: true
# Available themes: default | dark | forest | neutral
theme:
light: default
dark: dark

Examples

graph TD
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]

让你的Hexo-Next支持数学公式

注意在把next.config文件中的mathjax改为true之后还要在文章标题添加mathjax: true,这样仅在需要的界面渲染公式,可以提高加载速度

搜索

二分

35. 搜索插入位置(升序不重复)

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

  • 建议可以复刻一下算法过程,实际上当target < nums[mid] 时,返回值并不需要-1
  • .size()返回的是长度,要-1!
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Solution {
    public:
    int searchInsert(vector<int>& nums, int target) {
    int l=0, r=nums.size()-1; //注意size()
    int flag = 0;
    int mid = (l+r) /2;
    while(l<=r){
    if (nums[mid] == target){
    return mid;
    }
    if(nums[mid] < target)
    l = mid + 1;
    else
    r = mid - 1;
    if(l<=r) mid = (l+r) /2;
    }
    if(nums[mid] > target){
    return mid; //就是这里!画图复现一边代码就能看出来这里不-1
    }
    else
    return mid + 1;
    }
    };

34. 搜索指定数的重复区间(上下界bysearch)

常规二分(两遍时间复杂度不变)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int l = 0,r = nums.size()-1;
int mid1 = (l+r) / 2;
int mid2 = (l+r) / 2;
int f = 0,ans1,ans2;
while(l<=r){
if(nums[mid1] == target) ans1 = mid1,f=1; //需要判定区间边界是否要包含在内
if( nums[mid1] >= target)
r = mid1 -1;
else
l = mid1 + 1;
mid1 = (l+r) /2;
}
l = 0,r = nums.size()-1; //重复使用变量需要初始化
while(l<=r){
if(nums[mid2]==target)
ans2 = mid2;
if( nums[mid2] <= target)
l = mid2 + 1;
else
r = mid2 -1;
mid2 = (l+r) /2;
}
if(f == 1) //不知道为啥这里判定就是会报错。。
return {ans1, ans2};
return {-1,-1};
}
};

bysearch解法

思路:找到一个大于等于target的最小下标,再找一个大于target的最小下标

  • 把不一样的问题整理成一样的问题可以减少代码量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class Solution { 
    public:
    int binarySearch(vector<int>& nums, int target, bool type){
    //bool用来判断是大于等于还是大于,type=1时筛选大于等于
    int l = 0,r = nums.size()-1,ans=-1;
    int mid = (l+r)/2;
    while(l<=r){
    if(nums[mid]==target) ans=mid;//mid是一个中间量,需要ans来记录符合条件的值
    if(nums[mid]>target || (type && nums[mid] >= target)){
    r = mid -1;
    }
    else
    l = mid +1;
    mid =(l+r)/2;
    }
    return ans;
    }
    vector<int> searchRange(vector<int>& nums, int target) {
    int left = binarySearch(nums,target,1);
    int right = binarySearch(nums,target,0);
    if(left >= 0)
    return {left,right}; //vector类型的返回方式
    else
    return {-1,-1};
    }
    };

977.有序数组的平方(双指针)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int l = 0, r = nums.size()-1;
vector<int> ans(nums.size());
int i = nums.size()-1;
while(i >= 0){
int res1 = nums[l]*nums[l];
int res2 = nums[r]*nums[r];
if(res1 > res2){
ans[i] = res1;
l++;
}
else{
ans[i]=res2;
r--;
}
i--;
}
return ans;
}
};

滑动窗口

209.长度最小的子数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX;
int sum = 0; // 滑动窗口数值之和
int i = 0; // 滑动窗口起始位置
int subLength = 0; // 滑动窗口的长度
for (int j = 0; j < nums.size(); j++) {
sum += nums[j];
// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
while (sum >= s) {
subLength = (j - i + 1); // 取子序列的长度
result = result < subLength ? result : subLength;
sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
//先减随后i自加
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
一个我写的的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i=0,ans=100001;
int sum = 0;
for(int j=0;j<nums.size();j++){
sum += nums[j];
while(sum>=target && i<=j){
int count = j-i+1;
if(ans>count) ans=count; //注意先判定更新ans再操作
sum -= nums[i];
i++;
}
}
if(ans==100001) ans=0;
return ans;
}
};

438.找到字符串中所有字母异位词

啊啊啊啊啊啊啊啊啊啊啊啊为什么不对啊啊啊啊啊啊啊啊啊啊啊啊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int m=s.length(),n=p.length();
int pp=0;
vector<int> ans;
while(pp<(m-n)){
int a[30]={0},flag=1;
for(int i=0;i<m;i++){
a[p[i]-'a']++;
}
for(int i=pp;i<pp+n-1;i++){
a[p[i]]--;
}
for(int i=0;i<26;i++){
if(a[i]!=0) {
flag=0;
break;
}
}
if(flag){
ans.push_back(pp);
}
}
return ans;
}
};

904.水果成篮(好难捏)

官方解

我们可以使用滑动窗口解决本题,$\textit{left}$ 和 $\textit{right}$ 分别表示满足要求的窗口的左右边界,同时我们使用哈希表存储这个窗口内的数以及出现的次数。

我们每次将 right\textit{right}right 移动一个位置,并将 fruits[right]$\textit{fruits}$[$\textit{right}$]fruits[right] 加入哈希表。如果此时哈希表不满足要求(即哈希表中出现超过两个键值对),那么我们需要不断移动 left\textit{left}left,并将 $fruits[left]\textit{fruits}[\textit{left}]fruits[left]$ 从哈希表中移除,直到哈希表满足要求为止。

需要注意的是,将 $fruits[left]\textit{fruits}[\textit{left}]fruits[left]$ 从哈希表中移除后,如果 $fruits[left]\textit{fruits}[\textit{left}]fruits[left]$ 在哈希表中的出现次数减少为 000,需要将对应的键值对从哈希表中移除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int n = fruits.size();
unordered_map<int, int> cnt;

int left = 0, ans = 0;
for (int right = 0; right < n; ++right) {
++cnt[fruits[right]];
while (cnt.size() > 2) {
auto it = cnt.find(fruits[left]);
--it->second;
if (it->second == 0) {
cnt.erase(it);
}
++left;
}
ans = max(ans, right - left + 1);
}
return ans;
}
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/fruit-into-baskets/solutions/1893352/shui-guo-cheng-lan-by-leetcode-solution-1uyu/
来源:力扣(LeetCode
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
详解

无注释版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int totalFruit(vector<int>& fruits) {
if(fruits.size()<3) return fruits.size();
int mn=0;
for(int j=0,f1=0,f2=0,t=0;j<fruits.size();j++){
if(fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2]){
if(f1!=f2) f1=t;
f2=j;
}
if(fruits[t]!=fruits[j]) t=j;
mn=mn>(j-f1+1)?mn:(j-f1+1);
}
return mn;
}
};

有注释版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public:
int totalFruit(vector<int>& fruits) {
//当fruits数组长度≤2,则直接返回fruits数组长度
if(fruits.size()<3) return fruits.size();
//ans为结果,即fruits数组中包含两个不同水果类型的最大子序列长度
int ans=0;
/*j为遍历fruits数组的指针,也是滑动窗口的右边界,f1,f2分别为第一个和第二个篮子
的起始索引,f1同时也是滑动窗口的左边界,t为未来两个篮子里的第一个篮子的起始索
引*/
for(int j=0,f1=0,f2=0,t=0;j<fruits.size();j++)
{
/*当f1=f2时,fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2]说明遇到的是第二个篮子
要装的第二种水果;当f1!=f2时,fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2]说明
遇到的是第三种水果*/
if(fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2])
{
/*当f1=f2时,说明第一个篮子已经装了一种水果,第二个篮子里还没有装,不更新f1,
只更新f2,即往第二个篮子里装第二种水果;
当f1!=f2时,fruits[j]!=fruits[f1]&&fruits[j]!=fruits[f2]说明遇到的是第三种水果,
则更新f1和f2,即当前两个篮子的两种水果已装满,更新f1和f2为未来两个篮子里的两种
水果*/
if(f1!=f2) f1=t;
f2=j;
}
/*t寻找未来两个篮子中第一个篮子要装的一种水果的起始索引,每次t与当前的j作对比,
当fruits[t]!=fruits[j]时,更新t=j*/
if(fruits[t]!=fruits[j]) t=j;
/*更新当前子序列的长度,即每次计算装入两个篮子中的水果总数目*/
ans=ans>(j-f1+1)?mn:(j-f1+1);
}
return ans;//返回最终结果ans
}
};
一个部分错误的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int tr1,tr2,i=0;
int ans=0;
for(int j=1;j<fruits.size();j++){
int count=1;
tr1 = fruits[i];
while(1){
if(fruits[j]!=tr1){
count++;//用两个数表示两个果很容易错
if(count == 2)
tr2 = fruits[j];
else{
if(fruits[j]!=tr2)
break;
}
}
if(j==fruits.size()-1)
break;
j++;
}
int length = j-i+1;
if(length>ans)
ans=length;
i++; //j在到达最后一位时自动退出循环了,但是有可能j不动i后移一位时会得到最长
//这个故事告诉我们要在while窗口中更新i
}
return ans;
}
};

76.最小覆盖字串

哈希表

判断集合中某个元素存在性

49.字母异位词分组

官方题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for (string& str: strs) {
string key = str;
sort(key.begin(),key.end());
mp[key].emplace_back(str);
}
vector<vector<string>>ans;
for(auto it=mp.begin();it!=mp.end();++it){
ans.emplace_back(it->second);
}
return ans;
}
};

438. 找到字符串中所有的字母异位词

啊啊啊啊啊改了无数次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int m=s.size(),n=p.size();
int pp=0,count=0;
vector<int> ans;
while(pp<(m-n+1)){ //都是循环条件没找好
int a[30]={0},flag=1;
for(int i=0;i<n;i++){
a[p[i]-'a']++;
}
for(int i=pp;i<pp+n;i++){
a[s[i]-'a']--;
}
for(int i=0;i<26;i++){
if(a[i]!=0) {
flag=0;
break;
}
}
if(flag){
ans.push_back(pp);
}
pp++;
}
return ans;
}
};

模拟

##循环

螺旋矩阵

  1. 循环不变量,区间左右开闭的选择守恒
  2. 注意边界限定,以及lr同时赋begin值
  3. 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    class Solution {
    public:
    vector<vector<int>> generateMatrix(int n) {
    vector<vector<int>> a(n, vector<int>(n, 0)); // vector类型创建方法
    int l=0,r=0,i=1;
    int begin=0,end=n-1; //
    int loop=n/2;
    while(loop--){ //
    l=begin,r=begin; //
    for(;r<end;r++){
    a[l][r] = i;
    i++;
    }
    for(;l<end;l++){
    a[l][r] = i;
    i++;
    }
    for(;r>begin;r--){
    a[l][r] = i;
    i++;
    }
    for(;l>begin;l--){
    a[l][r] = i;
    i++;
    }
    begin++;
    end--;
    }
    int mid=n/2;
    if (n % 2) { //
    a[mid][mid] = i;
    }
    return a;
    }
    };

Kapla-Meier法

是一种刻画数据的非参数生存函数,直接用概率乘法定理估计生存率,但是不能建立数字模型对多个影响因素进行分析。
主要思路:
$$
S(t_i)=S(t_{i-1})(1-\frac{d_i}{n_i})
$$

代码:查看总体数据的生存曲线

1
2
3
4
5
6
7
8
9
fig, ax = plt.subplots(figsize=(10,8)) 
kmf = KaplanMeierFitter()
kmf.fit(data.tenure, # 代表生存时长
event_observed=data.Churn, # 代表事件的终点
)

kmf.plot_survival_function(at_risk_counts=True,ax=ax)
plt.show()

一个对比AI影像和传统影像的论文,介绍各自的特征
深度学习简单直接自动化,大力出奇迹;影像组学步步为营靠人工,识珠靠慧眼。两种方法本质上都依赖大数据,都可以划分为图像预处理、特征筛选和特征建模三个阶段。

传统影像组学

方法上,影像组学提取传统的图像特征,包括形状、灰度、纹理等,采用传统统计(模式识别)模型来分类和预测,如支持向量机、随机森林、XGBoost等;
Overview
Img

AI影像组学

深度学习人工智能模型

放射组学基于人工智能治疗预后
DL方法基于深度神经网络进行模式识别,模式识别通常包括一系列可训练的非线性操作,称为层,每个层将输入数据转换为便于模式识别的表示。随着越来越多的层对输入数据应用转换,这些数据越来越多地抽象为深层特征表示。由此产生的深层特征最终可以由网络的最后一层转化为所需的输出,例如治疗结果的可能性或肿瘤的分子亚型。深度学习是一个广泛的、技术性的、动态发展的领域。我们简要介绍了基于预测的AI影像学中最常见的主题,并对深层神经网络的类型、常用框架、解决数据局限性的方法进行了更详细的补充讨论。所有这些问题在别的研究中都有综述。

VGG实现特征提取

常用代码&注解

查看模型结构

1
2
3
4
5
6
# 查看模型整体结构
structure = torch.nn.Sequential(*list(vgg_model.children())[:])
print(structure)

# 查看模型各部分名称
print('模型各部分名称', vgg_model._modules.keys())

运行后可知:

  • vgg19整体结构分为三大部分:

    • ‘features’:上面输出的VGG19模型结构中的第一个Sequential,包含(0)-(36)层;
    • ‘avgpool’:VGG19模型结构的第二个部分AdaptiveAvgPool2d;
    • ‘classifier’:VGG19模型结构的最后一个部分Sequential,包含(0)-(6)层。注意其中Linear特征维度是1000,其余均为4096.

修改模型结构

  1. 可以获取各个部分,切割层数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 获取vgg19模型的第一个Sequential, 也就是features部分.
    features = torch.nn.Sequential(*list(vgg_model.children())[0])
    print('features of vgg19: ', features)

    # 获取vgg19模型的最后一个Sequential, 也就是classifier部分.
    classifier = torch.nn.Sequential(*list(vgg_model.children())[-1])
    print('classifier of vgg19: ', classifier)

    # 在获取到最后一个classifier部分的基础上, 再切割模型, 去掉最后一层.
    new_classifier = torch.nn.Sequential(*list(vgg_model.children())[-1][:6])
    print('new_classifier: ', new_classifier)
  2. 用new_classifier替换vgg19原始模型中的分类器( classifier )部分,就得到了输出维度是4096的VGG19模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import torch
    import torchvision.models as models
    from torchvision import transforms
    from PIL import Image

    # 获取vgg19原始模型, 输出图像维度是1000.
    vgg_model_1000 = models.vgg19(pretrained=True)

    # 下面三行代码功能是:得到修改后的vgg19模型.
    # 具体实现是: 去掉vgg19原始模型的第三部分classifier的最后一个全连接层,
    # 用新的分类器替换原始vgg19的分类器,使输出维度是4096.
    vgg_model_4096 = models.vgg19(pretrained=True)
    new_classifier = torch.nn.Sequential(*list(vgg_model_4096.children())[-1][:6])
    vgg_model_4096.classifier = new_classifier

    # 获取和处理图像
    image_dir = '/mnt/image_test.jpg'
    im = Image.open(image_dir)
    trans = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])
    im = trans(im)
    im.unsqueeze_(dim=0)

    # 使用vgg19得到图像特征.
    # 原始vgg19模型
    image_feature_1000 = vgg_model_1000(im).data[0]
    print('dim of vgg_model_1000: ', image_feature_1000.shape)

    # 修改后的vgg19模型
    image_feature_4096 = vgg_model_4096(im).data[0]
    print('dim of vgg_model_4096: ', image_feature_4096.shape)
  3. 简化版本:只需要原始vgg19模型的第一个部分features部分的输出结果.但是只适用于联网加载的vgg19模型(即设置了pretrained=True的模型),不适用于使用了本地vgg19模型的vgg_model

    1
    2
    3
    4
    5
    6
    7
    # 获取vgg19原始模型的features部分的前34个结构, 得到新的vgg_model模型.
    vgg_model = models.vgg19(pretrained=True).features[:34]

    # 但是下面的代码只能得到classifier部分的前40个,
    # 而不能得到包含features及avgpool及classifier的一共前40个结构.
    # 所以这个方法不能实现输出4096维度图像特征的目标.
    # vgg_model = models.vgg19(pretrained=True).classifier[:40]
  4. 一些小tips

    1. 修改特征类型
      1
      2
      `VGG16_Weights.IMAGENET1K_FEATURES`
      改成`VGG16_Weights.IMAGENET1K_V1`

用于预测的卷积神经网络

在放射学中,大多数基于DL的生物标记物应用都使用卷积神经网络从成像数据得出预测(图3a)。卷积神经网络是神经网络的一种特殊形式,用于学习图像的空间特征,并且由于它们在诊断任务中的表现,受到了广泛的关注。在几项引人注目的研究中,基于CNN的模型在诊断胸片和CT以及数字乳房X光摄影方面甚至超过了专业的人类专家。正如CNN已被证明能够学习指示恶性肿瘤的图像特征一样,越来越多的研究表明,它们可以根据与预后、风险和分子特征相关的肿瘤性质的细微差异对患者进行分型(图3a)。当使用患者结果数据进行训练时,CNN的卷积层可以学会识别反映预后的新成像表型。CNN可以应用于2D或3D输入,并且可以使用多个输入,以便从图像类型的组合中学习,例如多参数或动态MRI扫描。大量的CNN结构可以选择来用于基于AI的生物标记物研究(补充表2)。补充框2中进一步详细讨论了它们的原理和优势。
Img

  • 用于预测的卷积神经网络(CNN)模型示例。输入图像或体积通过CNN层,CNN层执行操作并将其转换为目标输出向量。卷积层是将成像数据转换为深度特征表示的一组操作。每个过滤器通过图像,并与非线性激活函数配对,以强调特定任务感兴趣的视觉模式。随着更多的卷积层层叠,CNN可以在图像中学习到更复杂的视觉模式。在整个CNN分类器中,深度特征通过池化操作定期聚合。经过卷积层和池化层处理后,深度特征表示最终被展平为向量。接下来,完全连接的层将这些CNN衍生的图像特征转换为对应于目标输出的向量。这些模型可用于预测治疗反应、预测、肿瘤亚型和生物标志物的分类以及生理值的预测。

  • 全卷积神经网络是一种CNN类型,只包含产生图像输出的卷积层,如肿瘤位置图。

  • 完全连接的网络可以根据非图像数据进行训练,如影像组学特征和临床变量。

  • 当对模型进行结果预测训练时,对大量数据的需求可能会存在局限性,在这种情况下,可行的患者数据可能比诊断研究更加有限。幸运的是,尽管训练数据很少,但有几种方法可以利用神经网络的优势。例如,迁移学习,其中针对一个模式识别任务训练的模型被重新调整用途以执行新任务,经常用于在训练数据大幅减少的情况下实现强大的CNN性能其他方法可用于处理有限或有缺陷的训练数据。

一些论文

一个特征提取网络的介绍

Deep Feature——用深度网络来提取特征

Pure DL直接使用Autoencoder来提取特征,然后直接进行分类。

采用LIDC中已经标记(分割)好的肺结节病灶区域作为ROI,对ROI的最小包围矩形区域进行尺寸归一化,然后输入一个5层Autoencoder网络进行特征编码,对Autoencoder网络进行训练。之后,使用训练优化完成后的5层网络中的第4层网络输出,共200个特征向量,输入一个二值分类树,区分肺结节良恶性。
Img
但是这种方法准确度有待考量。

DL+Tr可以预测生存周期

在传统的影像组学三大件(形状、灰度、纹理)之上,又添加了来自于深度学习网络的深度特征(Deep Feature)。所使用的深度学习网络包含5个卷积层和3个全连接层。倒数第二个(full7)和倒数第三个(full6)全连接层的输出,共8192个特征,与其他影像组学特征一起输入预测模型,来预测多形性成胶质细胞瘤患者的生存周期
Img

DRL

直接采用CNN网络的输出作为特征,连接传统的分类器(预测器)进行分类和预测。作者在MR多模态影像上,测试和验证了DLR预测低级别胶质瘤突变分级的准确性。下方依次给出的是整体流程图,以及将DLR与传统影像组学方法的特异性、敏感性等对比、ROC对比。从对比可以看出,DLR方法在各项指标中几乎全面胜出。并且,对比中发现,采用多模态影像,并且对特征进行进一步筛选,得到的各项指标是最高的。
Img
Img
Img

VGG-16 Architecture for MRI Brain Tumor Image Classification

  • 初始阶段是对样本图像进行预处理,然后使用池化层进行过滤,使用卷积层进行特征提取,最后使用架构模型的 FC 层进行分类。来自分子脑肿瘤数据库(REpository of Molecular BRAin Neoplasia DaTa,REMBRANDT)的核磁共振图像使用了预先训练好的模型架构,如视觉几何组(VGG)VGG-19、VGG-16、Inception-V3、Inception-V2、残差网络(ResNet)ResNet-18 和 ResNet-50。
  • The use of DL and TL methods to diagnose and classify brain tumors using MRI brain images has been proved to be a promising methodology.contains many CNN designs, including ResNet,Inception, and VGG networks.
  • Figure 2 illustrates the CNN model’s architecture for brain tumor categorization.Img
    • 从大脑图像中提取的深度特征被输入到设计的具有预训练特征的 CNN 模型中。**(GoogleNet)Inception-v2、Inception-V3、ResNet-18 [14]、ResNet-50 VGG-19 和 VGG-16 是本研究中使用的六个预训练 CNN 模型**。
    • 最初,VGG-16 和 VGG-19 架构模型都将核磁共振成像脑图像作为输入。深度特征从网络的卷积层和最大池化层提取。随着深度的增加,分类误差也随之减少,直到达到 19 层时,分类误差才趋于饱和。研究人员还验证了深度在图形表示法中的重要性。
    • CNN performs better in larger datasets than the smaller ones. When it is not possible to produce a big training dataset, TL can be utilized.
  • Conclusion: 具有 16 个网络层的 VGG-16 架构在将 MR 脑图像分类为肿瘤或正常图像方面提供了更高的准确度、精确度、F1 分数和召回率。

二者比较

深度学习并没有完全取代影像组学。主要的原因还是数据集规模的限制。

深度学习能够大幅提高分类或预测模型的准确性,但这是有代价的。相比影像组学,深度学习方法需要更多的训练数据。但影像组学所研究的问题往往是某种肿瘤的分期或分型,或者是预后生存率,此类问题的训练数据(Grand Truth数据)收集成本是非常高昂的,一般需要病理或术后随访来进行验证。因此,训练数据集的规模通常数量级在几百,与深度学习常见的数千、上万级数据集相差很远。

可以采用深度学习中普遍采用的数据增强手段(Data Augmented)了。这在一定程度上弥补了数据缺口。而数据增强在传统的影像组学中是无法采用的。

Seaborn

详解seaborn,各种类型的图样!
Seaborn基本是一个建立于Matplotlib基础上的一个2D绘图库。Seaborn创建在Python核心可视化库Matplotlib的基础上,它旨在作为补充,而不是替代,但是,Seaborn具有一些非常重要的函数。让无涯教程在这里看到其中的一些。函数有助于:

  • 内置用于设置matplotlib图形样式的主题
  • 可视化单变量和双变量数据
  • 拟合并可视化线性回归模型
  • 绘制统计时间序列数据
  • Seaborn与NumPy和Pandas数据结构配合良好
  • 它带有用于为Matplotlib图形设置样式的内置主题

入门

导入库&数据集

1
2
3
4
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sb
import numpy as np
  1. 默认情况下数据集作为Pandas DataFrame加载
    1
    2
    df=sb.load_dataset('tips')
    print df.head() #可以输出列表
  2. 查看可用数据集:
    1
    print sb.get_dataset_names()
  3. DataFrame以矩形网络的形式存储数据,每一行都包含一个值,每一列都是一个向量,用于保存特定变量的数据。
    行不需要包含相同数据类型的值!数字字符逻辑都可以。

简单的呈现代码!

1
2
3
4
5
6
7
def sinplot(flip=1):
x=np.linspace(0, 14, 100)
for i in range(1, 5):
plt.plot(x, np.sin(x + i * .5) * (7 - i) * flip)
#sb.set() #更改为Seaborn的默认值,背景上会有灰色的网格,不加就是纯白色背景
sb.displot()
plt.show()

Seaborn带有自定义主题和高级页面,用于自定义和控制外观。
注释后是默认Matplotlib图,注释前是Seaborn默认图。

displot & histplot

一个对于displot&histplot呈现方式的官方文档说明

  • displot可以呈现直方图(histogram)和有层理的核密度估计图(KDE kernel density estimate),y轴是密度
  • histplot会展现一个更标准的计数直方图,y轴是计数
  • 默认情况下,distplot 使用 Freedman-Diaconis 规则,但显示的条形图不超过 50 个。histplot 完全将二进制选择委托给 numpy 的 “自动 “默认设置,它可以根据数据特征使用不同的参考规则,并且可以在大型数据集中选择非常窄的条形宽度。您可能需要指定上限(bins=50),以完全重现以前的图表。
  • 状态设置参考:
    参数 类型 含义
    hist bool 是否使用直方图
    xlabel/ylabel str x,y的坐标含义
    kde bool 是否启用核心密度估计,可以让直方图和曲线比较好地耦合,但是如果想要更好就写入stat = "density";kde曲线会溢出直方图,只覆盖histplot中数据地实际范围,更宽的范围展示可以使用kde_kws = dict(cut=3)
    rug bool 是否添加底部阴影
    stat density 纵轴默认为count 设定后为密度
    common_norm cool 若为True,则直方图高度显示频率而非计数
    ``
  • 一种直方图与曲线更好拟合的方法
    1
    2
    3
    4
    5
    sns.histplot(
    df["flipper_length_mm"], kde=True,
    stat="density", kde_kws=dict(cut=3),
    alpha=.4, edgecolor=(1, 1, 1, .4),
    )

kdeplot

  • 状态设置参考:
    参数 类型 含义
    shade bool 是否添加曲线下阴影

界面样式设置

整体图的操作

操作 代码 可设定值
主题样式 .setstyle() (双引号引出)darkgrid/whitegrid/dark/white/ticks
刻度删除 sb.despine()
缩放绘图元素、控制图比例 set_context 四个用于上下文的预设模板,基于相对大小,上下文命名:paper/notebook/talk/poster
自定义样式 传参给set_style() print(sb.axes_style)
输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{'axes.axisbelow'     : False,
'axes.edgecolor' : 'white',
'axes.facecolor' : '#EAEAF2',
'axes.grid' : True,
'axes.labelcolor' : '.15',
'axes.linewidth' : 0.0,
'figure.facecolor' : 'white',
'font.family' : [u'sans-serif'],
'font.sans-serif' : [u'Arial', u'Liberation
Sans', u'Bitstream Vera Sans', u'sans-serif'],
'grid.color' : 'white',
'grid.linestyle' : u'-',
'image.cmap' : u'Greys',
'legend.frameon' : False,
'legend.numpoints' : 1,
'legend.scatterpoints': 1,
'lines.solid_capstyle': u'round',
'text.color' : '.15',
'xtick.color' : '.15',
'xtick.direction' : u'out',
'xtick.major.size' : 0.0,
'xtick.minor.size' : 0.0,
'ytick.color' : '.15',
'ytick.direction' : u'out',
'ytick.major.size' : 0.0,
'ytick.minor.size' : 0.0}

示例:

1
sb.set_style("darkgrid", {'axes.axisbelow': False})

调色板

  1. color_palette()可以直接为绘图赋予颜色并添加更多的美学价值
    1
    seaborn.color_palette(palette=None, n_colors=None, desat=None)
  • n_colors是调色板的颜色数,如果为None,则默认值取决于如何制定调色板,默认状态下为六种颜色。
  • desat指每种颜色去饱和的比例。
  • 返回值是RGB元组的列表
  • 现成调色板:Deep``muted``bright``pastel``dark``colorblind
  1. seaborn.palplot()将调色板绘制为水平阵列。

定性调色板

1
2
3
current_palette=sb.color_palette()
sb.palplot(current_palette) #用于水平绘制颜色阵列
plt.show()

默认会看到六种颜色,可以给n_color传参来查看所需颜色的数量

顺序调色板

顺序图适合于表示范围内从相对较低值到较高值的数据分布
给color参数上加一个“s”会绘制顺序图

1
2
3
current_palette=sb.color_palette()
sb.palplot(sb.color_palette("Greens"))
plt.show()

分散调色板

不同的调色板使用两种不同的颜色,每种颜色代表值从任一方向上的公共点变化。
假定绘制范围为-1到1的数据,值-1到0代表一种颜色,0到+1代表另一种颜色。
默认情况下,值从零开始居中,您可以通过传递值来使用参数中心来控制它。

1
2
3
current_palette=sb.color_palette()
sb.palplot(sb.color_palette("BrBG", 7))
plt.show()

其中7表示调色板上有七种颜色。

设置默认调色板

函数 color_palette()具有一个名为 set_palette()的函数。
set_palette()color_palette()的参数相同,但默认的Matplotlib参数已更改,因此调色板可用于所有绘图

1
2
3
4
5
6
7
8
9
10
def sinplot(flip=1):
x=np.linspace(0, 14, 100)
for i in range(1, 5):
plt.plot(x, np.sin(x + i * .5) * (7 - i) * flip)

import seaborn as sb
sb.set_style("white")
sb.set_palette("husl")
sinplot()
plt.show()

绘制单变量分布

seaborn可以帮助理解数据的单变量分布
seaborn.sidtplot()提供最快查看单变量分布的方法,可以绘制适合数据和密度估计的直方图。

参数 remark
data 一维数组或列表
bins hist bins的规格
hist bool
kde bool

matplotlib画入pyqt5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import sys
import matplotlib.pyplot as plt
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas

class MatplotlibWindow(QMainWindow):
def __init__(self):
super().__init__()

self.initUI()

def initUI(self):
self.setWindowTitle('Matplotlib in PyQt')

# 创建Matplotlib图形
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)

# 创建一个垂直布局并将Matplotlib图形添加到其中
layout = QVBoxLayout()
layout.addWidget(self.canvas)

# 创建一个QWidget并将布局设置为其主布局
main_widget = QWidget(self)
main_widget.setLayout(layout)
self.setCentralWidget(main_widget)

# 创建一个Matplotlib子图
self.ax = self.figure.add_subplot(111)
self.ax.set_title('Matplotlib in PyQt')

# 绘制一个简单的图形
self.ax.plot([1, 2, 3, 4], [10, 5, 20, 8])

# 更新Matplotlib图形
self.canvas.draw()

def main():
app = QApplication(sys.argv)
window = MatplotlibWindow()
window.show()
sys.exit(app.exec_())

if __name__ == '__main__':
main()

ITK

ITK在cmake上配置的一个非常棒的教程

VTK

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import numpy as np
import nibabel as nib
from ipywidgets import interact, interactive, IntSlider, ToggleButtons
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')
#%matplotlib inline
import seaborn as sns
sns.set_style('darkgrid')

def explore_3dimage(layer):
plt.figure(figsize=(10, 5))
plt.imshow(image_data[:, :, layer], cmap='gray');
plt.title('Explore Layers of adrenal', fontsize=20)
plt.axis('off')
return layer

image_path = "./brain1_image.nii.gz"
image_obj = nib.load(image_path)
print(f'Type of the image {type(image_obj)}')

image_data = image_obj.get_fdata()
type(image_data)

height, width, depth = image_data.shape
print(f"The image object height: {height}, width:{width}, depth:{depth}")
print(f'image value range: [{image_data.min()}, {image_data.max()}]')
print(image_obj.header.keys())

pixdim = image_obj.header['pixdim']
print(f'z轴分辨率: {pixdim[3]}')
print(f'in plane 分辨率: {pixdim[1]} * {pixdim[2]}')

z_range = pixdim[3] * depth
x_range = pixdim[1] * height
y_range = pixdim[2] * width
print(x_range, y_range, z_range)

maxval = 25
i = np.random.randint(0, maxval)
# Define a channel to look at
print(f"Plotting z Layer {i} of Image")
plt.imshow(image_data[0, :, :], cmap='gray')
plt.axis('off');

#interact(explore_3dimage, layer=(0, image_data.shape[-1]));
plt.show()

Vedo

Mesh

Mesh模型是一种用于描述三维物体表面的数学表示方法。它由一系列顶点(vertices)和连接这些顶点的线段或面片(faces)组成。每个顶点具有其在三维空间中的坐标位置,而每个面片则由一组顶点索引构成,定义了一个平面。
在Mesh模型中,可以使用不同的数据结构来表示顶点和面片之间的关系,常见的包括三角形网格(triangle mesh)和四边形网格(quadrilateral mesh)三角形网格是最常用的一种,它由三个顶点和一个法线向量(用于确定面片的朝向)组成。四边形网格则由四个顶点和一个法线向量构成。
除了顶点和面片信息,Mesh模型还可以包含其他属性,如颜色、纹理坐标、法线向量等。这些属性可以为模型添加更多的细节和真实感。
常使用.ply格式的文件存放mesh模型

一点报错

VTK

module ‘backend_interagg’ has no attribute ‘FigureCanvas’. Did you mean: ‘FigureCanvasAgg’?

解决:加入代码

1
2
import matplotlib
matplotlib.use('TkAgg')

解释
matplotlib.use('TkAgg') 这行代码是用于设置 Matplotlib 库的后端。后端是用于实现图形绘制和用户界面的库。Matplotlib 支持多种后端,例如 ‘TkAgg’‘Qt5Agg’‘GTK3Agg’ 等。

在这行代码中,‘TkAgg’ 是后端的名称,而 matplotlib.use() 函数用于指定使用哪种后端。在这个例子中,你告诉 Matplotlib 使用 ‘TkAgg’ 后端。

Matplotlib 默认使用 ‘TkAgg’ 后端,但有时你可能希望更改默认后端以利用特定 GUI 工具包的特性。例如,如果你希望使用 Qt5 而不是 Tkinter,你可以通过调用 matplotlib.use('Qt5Agg') 来实现。

需要注意的是,更改后端会影响所有后续的图形绘制,因此如果你在代码的不同部分使用不同的后端,你需要在每个需要使用特定后端的代码部分之前调用 matplotlib.use()

Vedo

AttributeError: ‘vtkmodules.vtkFiltersCore.vtkQuadricDecimation’ object has no attribute ‘MapPointDataOn’

提问区链接

1
pip install -U git+https://github.com/marcomusy/vedo.git

‘set’ object has no attribute ‘name’

在这里放个官方教程的链接先
文中目录分为两类:site root directory指根目录,也就是点进你的hexo文件夹的第一个目录;Next directory指主题目录,也就是点进Next文件夹后的第一个目录。

自定义特征

界面

  1. favicon:将图标存储于hexo/source/images下,小图标为16*16,中等为32*32

设置

cc协定设置(creative commons)

1
2
3
4
5
6
creative_commons:
license: by-nc-sa
size: small
sidebar: true
post: true
language: deed.zh

页面

添加

在根目录下执行

1
$ hexo new page [custom-page-name]

设置

通过设置根目录下的_config.yml管理页面,注意如果path在上文已使用要删去或改成/

1
2
3
4
index_generator:
path: archives
per_page: 10
order_by: -date

分类与标签

tag和categories是相同的操作,改个名就可以,以下以tag为例

添加标签页

  1. 根目录下执行
    1
    hexo new page tags
  2. 编辑新页面index如下:
    1
    2
    3
    4
    title: Tags
    date: 2014-12-22 12:39:04
    type: tags
    ---
  3. 向next设置界面的menu中加入
    1
    tags: /tags/ || fa fa-tags
  4. 默认的hexo配置文件会与上方教程冲突,需要在根目录设置文件中将这个设置关闭
    1
    2
    tag_generator:
    enable_index_page: false

标签页美化

  1. 修改next设置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    tagcloud:
    min: 12 # Minimun font size in px
    max: 20 # Maxium font size in px
    # 我喜欢小一点字所以设定的字偏小
    start: "#476D80"# Start color (hex, rgba, hsla or color keywords)
    end: "#98A9A3"# End color (hex, rgba, hsla or color keywords)
    #实际上是字体从小到大会有一个渐变,从start color变到end color,建议start颜色浅于end,会更好看一些,色系与主题周边色系相类似
    amount: 200 # Amount of tags, change it if you have more than 200 tags
    orderby: name # Order of tags
    order: 1 # Sort order

定义文章标签

在你文章标题下添加代码

1
tags: [Tag1,Tag2]

添加谷歌日历(Google Calendar)页

动态设置菜单栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
menu:
# 静态区
home: / || fa fa-home
archives: /archives/ || fa fa-archive
# 动态区
Docs: #动态栏名称
default: /docs/ || fa fa-book # 文件地址+图标,每个子菜单都需要一个默认界面
# 以下为两个子界面,排列在上方
Getting Started: #子界面名称,都是地址+标题
default: /getting-started/ || fa fa-flag
Installation: /installation.html || fa fa-download
Configuration: /configuration.html || fa fa-wrench
Third Party Plugins:
default: /third-party-services/ || fa fa-puzzle-piece
Math Equations: /math-equations.html || fa fa-square-root-alt
Comment Systems: /comments.html || fa fa-comment-alt

插件

Fancybox

展示音频,可以高度自定义的插件

1
fancybox: true

Busuanzi Counting(China)

将NexT设置文件中的相关按钮全打开,会在标题下显示本文总浏览量,页末显示网站的访问人数和浏览量

源代码美化

修改颜色

sidebar修改侧边栏字体颜色

next/source/css/_variables/主题名.styl下搜索sidebar
第一个是未选择项的颜色,第二个是鼠标高亮,第三个是选择高亮,我的设置如下:

1
2
3
$sidebar-nav-color                = #A9BACB;
$sidebar-nav-hover-color = #1e50a2;
$sidebar-highlight = #0482B5;

一些报错

hexo打开后页面一片空白

一开始的时候给我报错./soure/_data/header.swig中有一个变量不能调用,笨人看了同学的目录之后发现人家根本没这个文件夹…索性把这个文件夹删掉了,然后还是不行(但是不报错了),于是我去看了这个教程

QtDesiner的安装与配置

pyuic自动生成代码运行大法

1
2
3
4
5
class Ui_Dialog(QDialog):
def __init__(self, parent=None):
super(Ui_Dialog, self).__init__(parent)
self.showDialog(self)
def showDialog(self, Dialog):

动态加载已保存的界面

注意只有designer中已经保存的界面加载时才会更新

1
2
3
4
class MainForm(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.ui = uic.loadUi("new_UI_full.ui",self)

对于显示,运行操作,写在main函数里!
属性调用:
self.ui.pushButton.clicked.connect(self.bindButton)

槽函数与连接

注意事项(未解决)

如果使用自定义信号,一定要记得信号是类变量,必须在类中定义,不能在实例方法中定义,否则后面发射信号和连接槽方法时都会报错。
不过我是这么解决的:(强行定制函数ing)

1
2
3
4
self.dia.comboBox_ImgType.currentIndexChanged.connect(lambda: self.RadiomicsFunc.ImgType_init(
self.comboBox_ImgType.currentIndex())) # currentIndex返回当前项的序号(int),第一个项的序号为0
self.dia.comboBox_ImgType.currentIndexChanged.connect(lambda: self.stackedWidget.setCurrentIndex(
self.comboBox_ImgType.currentIndex()-1))#对应换页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   class configWin(ui_configWin.Ui_configWin,QtWidgets.QWidget):

def __init__(self,parent=None):
super().__init__(parent)
self.setupUi(self)
self.sizeChanged = QtCore.pyqtSignal(int)

def resize(self,width,height):
self.sizeChanged.emit(width)
print("sizeChanged....")
super().resize(width,height)

class mainWin(QtWidgets.QMainWindow,ui_mainWin.Ui_mainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.configWin = configWin(self)
self.configWin.sizeChanged.connect(self.sizeChanged)

报错AttributeError: 'builtin_function_or_method' object has no attribute 'connect'
修正:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QWindow
class configWin(ui_configWin.Ui_configWin,QtWidgets.QWidget):

sizeChanged =pyqtSignal(int)#写在这里

def __init__(self,parent=None):
super().__init__(parent)
self.setupUi(self)


def resize(self,width,height):
self.sizeChanged.emit(width)
super().resize(width,height)

class mainWin(QtWidgets.QMainWindow,ui_mainWin.Ui_mainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.configWin = configWin(self)
self.configWin.sizeChanged.connect(self.sizeChanged)

关于.connect()函数

需要注意的是,如果你的connect()里边是一个函数,以及你的函数传参是函数,你需要注意你书写函数的格式
一个改了很久的两行:

1
2
timer.timeout.connect(lambda: Bar.setValue(Bar.value()+10)) # 设置参数
timer.timeout.connect(lambda: checkState(Bar.value(),timer))# 检查时间
  1. 报错:传入的组件参数是NoneType
    这就需要区分progressBar.setValue()progressBar.setValue的区别:
  • 前者其实默认是一种call(None),而返回值是NoneType
  • 后者是一个函数(Reference the Method)
  1. 关于lambda
  • 可以认为是函数的声明,整个式子其实是对函数的调用,但是在.connect()中调用函数时,必须要使用该声明

讨论区有一个类似的例子

几种连接方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 第一种信号与槽连接的方法
cb_font = QFontComboBox(currentFontChanged=self.changeLabelFont)
cb_font.pyqtConfigure(objectName='fontCombo', editable=False)
# 第二种信号与槽连接的方法
cb_font.currentFontChanged.connect(self.changeButtonFont)
main_layout.addWidget(cb_font)

label = QLabel()
label.pyqtConfigure(text='示例文本!', objectName='label')

main_layout.addWidget(label)

closeButton = QPushButton('关闭')
# 第三种连接信号与槽的方法
closeButton.pyqtConfigure(objectName='button', clicked=self.close)
main_layout.addWidget(closeButton)

vhbox = QVBoxLayout()
vhbox.addLayout(main_layout)
vhbox.addStretch(1)
self.setLayout(vhbox)

# 第四种连接信号与槽的方法
QMetaObject.connectSlotsByName(self)

MainWindow(主窗口)系列设置

菜单键的相关函数

self.menu_1.setTitle(_translate(“MainWindow”, “####”)) #菜单名称
self.action_1.setText(_translate(“MainWindow”, “#####”)) #菜单选项
self.action_1.triggered.connect(self.actionHandler_1) #点击菜单选项绑定响应(def 函数)

提供四种Action信号

changed():修改Action的属性时触发(如修改toolTip的信息)
hovered():Action关联的菜单项或者鼠标停留或者按下快捷键时触发
toggled(bool checked):Action设置checkable属性,关联的菜单项或toolBar在点击后会改变选中状态,触发toggled信号,参数为是否选中的最新状态。
triggered(bool checked):鼠标点击或者快捷键时触发。

  • 给动作绑定事件:
    self.savelog.triggered.connect(self.saveLogs)
    其中self.saveLog是动作名称,self.aveLogs是需要绑定的事件。
    表示self.saveLog被触发时调用self.aveLogs函数事件

Scroll Area

设置 ScrollAreaWidgetContents 的minimumSize属性,只有窗口大小小于这个值的时候,滑块才生效。
实际上相当于scrollArea内部创建了一个子区域,如果子区域大于最外区域,则出现滑块,如果只想出现一边有滑块,没有的那一边建议子块的最小值设置为0

ComboBox

访问QComboBox的列表项

1
2
3
4
5
6
int currentlndex():返回当前项的序号,第一个项的序号为0
QString currentText():返回当前项的文本
QVariant currentData(int role = Qt::UserRole):返回当前项的关联数据
QString itemText(int index) 返回指定索引号的项的文本
QVariant itemData(int index, int role = Qt%:UserRole) 返回指定索引号的项的关联数据。
int count():返回项的个数。

槽函数

1
2
3
4
5
6
7
8
9
10
11
self.reco_comboBox.currentIndexChanged.connect(
lambda: self.model_init(self.reco_comboBox.currentIndex()))


#model_init函数的实现的核心代码
# 加载相关参数,并初始化模型
def model_init(self,tag):
if tag == 1: #当下拉框选中"1"触发事件
"""
代码省略
"""

QSlider

重写slider点哪里到哪里

新建包mySlider(或者直接新建类也行),就差个导入么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QSlider


class MySlider(QSlider): # 继承QSlider
customSliderClicked = pyqtSignal(str) # 创建信号

def __init__(self, parent=None):
super(QSlider, self).__init__(parent)

def mousePressEvent(self, QMouseEvent): # 重写的鼠标点击事件
super().mousePressEvent(QMouseEvent)
pos = QMouseEvent.pos().x() / self.width()
self.setValue(round(pos * (self.maximum() - self.minimum()) + self.minimum())) # 设定滑动条滑块位置为鼠标点击处
self.customSliderClicked.emit("mouse Press") # 发送信号

class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(487, 402)
self.horizontalSlider = QtWidgets.QSlider(Form) # 滑动条1
self.horizontalSlider.setGeometry(QtCore.QRect(60, 140, 361, 31))
self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal)
self.horizontalSlider.setObjectName("horizontalSlider")
self.horizontalSlider_2 = MySlider(Form) # 滑动条2,修改此处
self.horizontalSlider_2.setGeometry(QtCore.QRect(60, 230, 361, 31))
self.horizontalSlider_2.setOrientation(QtCore.Qt.Horizontal)
self.horizontalSlider_2.setObjectName("horizontalSlider_2")

self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)

def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))

主程序调用,connect是自定义slot

1
2
3
4
5
6
7
class MainWindow(Ui_Form, QMainWindow):  # 创建窗口
def __init__(self):
super().__init__()
self.setupUi(self)
self.horizontalSlider.valueChanged.connect(self.horizontalSlider_change) # 滑动条1数值变化时触发horizontalSlider_change
self.horizontalSlider_2.customSliderClicked.connect(
self.horizontalSlider_2_change)

美化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/*horizontal :水平QSlider*/
QSlider::groove:horizontal {
border: 0px solid #bbb;
}

/*1.滑动过的槽设计参数*/
QSlider::sub-page:horizontal {
/*槽颜色*/
background: rgb(90,49,255);
/*外环区域倒圆角度*/
border-radius: 2px;
/*上遮住区域高度*/
margin-top:8px;
/*下遮住区域高度*/
margin-bottom:8px;
/*width在这里无效,不写即可*/
}

/*2.未滑动过的槽设计参数*/
QSlider::add-page:horizontal {
/*槽颜色*/
background: rgb(255,255, 255);
/*外环大小0px就是不显示,默认也是0*/
border: 0px solid #777;
/*外环区域倒圆角度*/
border-radius: 2px;
/*上遮住区域高度*/
margin-top:9px;
/*下遮住区域高度*/
margin-bottom:9px;
}

/*3.平时滑动的滑块设计参数*/
QSlider::handle:horizontal {
/*滑块颜色*/
background: rgb(193,204,208);
/*滑块的宽度*/
width: 5px;
/*滑块外环为1px,再加颜色*/
border: 1px solid rgb(193,204,208);
/*滑块外环倒圆角度*/
border-radius: 2px;
/*上遮住区域高度*/
margin-top:6px;
/*下遮住区域高度*/
margin-bottom:6px;
}

/*4.手动拉动时显示的滑块设计参数*/
QSlider::handle:horizontal:hover {
/*滑块颜色*/
background: rgb(193,204,208);
/*滑块的宽度*/
width: 10px;
/*滑块外环为1px,再加颜色*/
border: 1px solid rgb(193,204,208);
/*滑块外环倒圆角度*/
border-radius: 5px;
/*上遮住区域高度*/
margin-top:4px;
/*下遮住区域高度*/
margin-bottom:4px;
}
QSlider::sub-page:horizontal {
background:rgb(170, 255, 127);
border-radius: 0.1px;
margin-top:9px;
margin-bottom:9px;
}
QSlider::add-page:horizontal {
background: rgb(170,255,127);
border-radius: 1px;
margin-top:9px;
margin-bottom:9px;
}

QCheckBox

常用方法

方法 描述
setChecked() 设置复选框的状态,True为选中,False为取消选中复选框
setText() 设置复选框的显示文本
text() 返回复选框的显示文本
isChecked() 检查复选框是否被选中
setCheckState() 设置复选框的勾选状态:2为选中(Checked);1为半选中(ParticallyChecked);0为没有选中(Unchecked)
setTristate(bool) 三态模式

isChecked()不能单独使用,要用stateChanged.connect连接起来
但是更建议使用toggle.connect()

1
2
3
4
5
6
self.dia.checkBox_2.stateChanged.connect(lambda: self.test())
def test(self):
chk1Status = self.dia.checkBox_2.text() + ", isChecked=" + str(
self.dia.checkBox_2.isChecked()) + ', chekState=' + str(
self.dia.checkBox_2.checkState()) + "\n"
print(chk1Status)

我改了无数次的代码是这样的,如果只是ischecked不会更新!!!
这个故事告诉我们,状态类的函数需要手动对他一直进行状态检查

1
2
3
4
self.checkBox1.stateChanged.connect(lambda: self.btnstate(self.checkBox1))
self.checkBox2.toggled.connect(lambda: self.btnstate(self.checkBox2))
self.checkBox3.stateChanged.connect(lambda: self.btnstate(self.checkBox3))
#btnstate为一个打印函数

常用信号

信号 描述
clicked(bool) 鼠标左键被按下,一直按着或者释放时,或者快捷键被按着或者释放时触发该信号
pressed() 当鼠标指针在按钮上并按下左键时触发该信号,一直按着或者按下并释放都会产生
released() 鼠标左键被释放时触发
toggled(checked) checkable设置为True时,状态发生改变时触发信号

测试代码

chk1Status = self.dia.checkBox_2.text() + ", isChecked=" + str( self.dia.checkBox_2.isChecked()) + ', chekState=' + str( self.dia.checkBox_2.checkState()) + "\n" print(chk1Status)
输出:
Normalization, isChecked=True, chekState=2

RadioButton

一个很不错的教程

  1. 单选框默认开启自动互斥(autoExclusive)。如果启用了自动互斥,属于同一个父部件的单选框的行为就和属于一个互斥按钮组的一样。如果你需要为属于同一父部件的单选框设置多个互斥按钮组,把它们加入QButtonGroup中。
  2. 每当一个按钮切换选中或未选中状态时,会发出的toggled()信号。如果希望每个按钮切换状态时触发一个动作,连接到这个信号。使用isChecked()来查看特定按钮是否被选中。
  3. 单选框可以显示文本,以及可选的小图标。图标使用setIcon()来设置,文本可以在构造函数或通过setText()来设置。可以指定快捷键,通过在文本中的特定字符前指定一个&。

QTextBrowser(文本浏览框)

适用于多行不可修改文字在UI中的呈现

修改函数

  1. 调用append方法可以向文本浏览框中添加文本
    1
    self.textBrowser.append("Hello World!") 

样式表

  • 不建议在designer里直接敲字,我发现这东西有一个雷点在于如果你直接往上边敲字是样式是没什么改变的,但是如果是append方法就可以按样式表的来

QPushBUtton

参数设置&样式表美化

  1. toolTip:在点击按钮时会鼠标下方会出现的小提示
  2. 蓝黑科技感样式表
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    QPushButton{
    background-color: transparent;
    color:rgb(137, 195, 235);
    border:1px solid #89c3eb;
    font: 75 13pt "Bahnschrift";
    }
    QPushButton:hover{
    color:rgb(53, 171, 239);
    border-radius:6px;
    border:2px solid #2980b6;
    }

    QPushButton:pressed{
    border: 1px solid #3C3C3C;
    border-radius:6px;
    background:rgb(124, 195, 255);
    }
  3. 当需要单独设置某个按钮的样式表时,如果有状态限制需要每一个后边都限定状态(比如下边的hover状态设置)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #pushButton_ori_2,#pushButton_ori,#pushButton_2,#pushButton{
    background-color: transparent;
    color:rgb(137, 195, 235);
    font: 75 13pt "Bahnschrift";
    border:none;
    }
    #pushButton_ori_2:hover,
    #pushButton_ori:hover,
    #pushButton_2:hover,
    #pushButton:hover{
    color:rgb(52, 231, 254);
    border:none;
    }

Example

好用的腾讯会议高仿例子:
最小化按钮样式表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
QPushButton{
background-color: rgb(255, 255, 255);
font: 15pt "宋体" ;
border:none;
border-radius:none;
color: gray;
}

QPushButton:hover{
background-color: rgb(218, 218, 218);
}

QToolTip{
background-color: rgb(255, 255, 255);
}

关闭按钮样式表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
QPushButton{
background-color: rgb(255, 255, 255);
font: 10pt "宋体" ;
border:none;
border-top-right-radius:4px;
border-top-left-radius:0px;
border-bottom-right-radius:0px;
border-bottom-left-radius:0px;
color: gray;
}

QPushButton:hover{
background-color: rgb(218, 218, 218);
}

QToolTip{
background-color: rgb(255, 255, 255);
}

设置按键样式表:
tip:把鼠标移到按钮上时的图片改为原图片的淡化版,大小35左右为佳。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
QPushButton{
background-color: rgb(255, 255, 255);
font: 10pt "宋体" ;
border:none;
border-top-right-radius:4px;
border-top-left-radius:0px;
border-bottom-right-radius:0px;
border-bottom-left-radius:0px;
color: gray;
}

QPushButton:hover{
background-color: rgb(218, 218, 218);
}

QToolTip{
background-color: rgb(255, 255, 255);
}

QProgressBar

常用方法

setRange(): 设置进度条的取值范围(最小值和最大值)
setMinimum(): 设置进度条的最小值
setFont(): 设置文本字体
setMaximum(): 设置进度条的最大值
setValue(): 设置进度条的值
reset(): 让进度条重新回到开始位置
setOrientation(): 设置进度条方向(水平: Qt.Horizontal, 垂直: Qt.Vertical)
setTextVisible(): 设置进度条的文本是否可见
setTextDirection(): 设置文本方向,只对垂直进度条有效
setInvertedAppearance(): 设置进度条的方向(True/False: 正反方向)
setFormat(): 设置文本字符串的格式(%p, 百分比显示,这是默认情况, %v: 当前进度, %m :总步数)

美化

1
2
3
4
5
6
7
8
9
10
11
QProgressBar {
background-color: rgb(114, 163, 195);
color:rgb(44, 62, 80);
border-style:none;
text-align:center;
border-radius:10px;
}
QProgressBar::chunk{
border-radius:10px;
background-color:qlineargradient(spread:pad, x1:0,y1:0.511364, x2:2, y2:0.523, stop:0 rgb(41, 128, 182), stop:1 rgb(43, 6, 255));
}

其中可以调整x2:2的数值,比如改为x2:1会使得渐变色变化速度更快且最终的颜色最深。

进度条更新并跳出新界面(可以做启动画面)

效果展示
原教程在这里,笨人对代码作了一些注释,后面附有修改

作者源代码(不完全)

背景建议使用QframeQprogressBar样式表设置参见上美化

  1. 设置定时器,实际上这个Bar并不是根据加载进度走的,而是100ms更新一次
    • 实际上如果你想与你的程序进度连接的话,只要把self.load_progress_bar放在你需要的位置就行
      1
      2
      3
      4
      def set_loader(self):
      self.timer = QtCore.QTimer() #建立一个计时器
      self.timer.timeout.connect(self.load_progress_bar)
      self.timer.start(100) #更新时间间隔
  2. 对进度条操作,每次加一,大于既定值时执行操作;此处是关闭本界面,调用打开新界面的函数,并停止计时
    1
    2
    3
    4
    5
    6
    7
    8
    def load_progress_bar(self):
    self.progressBar.setValue(self.progressBar.value() + 1)
    #self.cont_label_title[0] += 1 #这两行暂时不知道是什么意思,但是好像对功能实现没有影响
    #self.cont_label_title[1] += 1
    if self.progressBar.value() >= 100:
    self.window.close()
    self.open_table_main()
    self.timer.stop()
  3. 调用新界面产生函数
    1
    2
    3
    4
    5
    def open_table_main(self):
    self.MainWindow = QtWidgets.QMainWindow()
    self.ui = Ui_MainWindow()
    self.ui.setupUi(self.MainWindow)
    self.MainWindow.show()

笨人修改

QFileDialog

教程
获取文件夹路径,对话框获取文件

QLineEdit

常用API

  1. QLineEdit.text():返回输入框的当前文本。

  2. QLineEdit.addAction(Action,QLineEdit.ActionPosition):添加动作到文本输入栏,上面已经举过例子了。

  3. QLineEdit.setAlignment(Qt.Alignment flag):属性保存了输入框的对齐方式(水平和垂直方向。

  4. QLineEdit.setCompleter() :输入栏的自动补全就是靠这个实现的,下下章我们讲解。

  5. QLineEdit.deselect() :取消选中任何已选中的文本。

  6. QLineEdit.displayText():返回显示的文本。默认值为一个空字符串。

  7. setEchoMode():如果echoMode是Normal,和text()返回的一样;如果EchoMode是Password或PasswordEchoOnEdit,会返回平台相关的密码掩码字符;如果EchoMode是NoEcho,返回一个空字符串””。

  8. QLineEdit.selectedText():返回选中的的文本。如果没有选中,返回一个空字符串。默认为一个空字符串。

  9. QLineEdit.setCursorPosition(QLineEdit.cursorPosition):设置输入框当前光标的位置。

  10. QLineEdit.setMaxLength(int):此属性包含文本的最大允许长度。如果文本太长,将从限制的位置截断。默认值为32767。

  11. QLineEdit.setReadOnly(bool):此属性保存输入框是否为只读。在只读模式下,用户仍然可以将文本复制到剪贴板,但不能编辑它,且不显示光标。

  12. QLineEdit.setSelection(int start, int length) :从位置start选择文本为length个字符,允许负长度。我们一启动程序是否设置setSelection的,效果如下:

  13. QLineEdit.setValidator():设置输入框的验证器,将限制任意可能输入的文本

  14. placeholderText用于输入前的提示显示文字该属性包含行编辑的占位符文本。只要行编辑为空,设置此属性将使行编辑显示一个灰色的占位符文本。

通常情况下,即使具有焦点,空行编辑也会显示占位符文本。但是,如果内容是水平居中的,则行编辑具有焦点时,占位符文本不会显示在光标下方。默认情况下,该属性包含一个空字符串。

  1. QLineEdit.isClearButtonEnabled(bool) :是否设置清除内容的按钮。

  2. QLineEdit.setInputMask():设置掩码,效果就是我们演示视频中的License输入。

信号

  1. selectionChanged() :只要选择改变这个信号就会被发射。

  2. cursorPositionChanged(int old, int new) :只要光标移动,这个信号就会发射。前面的位置old,新的位置是new。

  3. editingFinished():按下返回或回车键或线条编辑失去焦点时发出此信号。

  4. returnPressed():按下返回或回车键时发出此信号。

  5. textChanged(str):只要文字发生变化就会发出此信号。文本参数是新文本。与textEdited()不同,当通过调用setText()以编程方式更改文本时,也会发出此信号。

  6. textEdited(str) :无论何时编辑文本都会发出此信号。文本参数是新文本。与textChanged()不同,当以编程方式更改文本时,不会发出此信号,例如通过调用setText()。

函数

  1. clear() :清除输入框内容

  2. copy():如果echoMode()是Normal,将选中的文本复制到剪贴板。

  3. cut() :如果echoMode()是Normal,将所选文本复制到剪贴板并删除它。 如果当前的验证不允许删除选定的文本,cut()将复制而不删除。

  4. paste() :如果输入框不是只读的,插入剪贴板中的文本到光标所在位置,删除任何选定的文本。如果最终的结果不被当前的验证器接受,将没有任何反应。

  5. redo() :重做上次操作,如果redo可用(isRedoAvailable() )。

  6. selectAll() :选中所有文本(即:高亮),并将光标移动到末尾。当一个默认值被插入时,这非常有用,因为如果用户在点击部件之前就输入,选中的文本将被删除。

  7. setText(str) :设置输入框显示的文本。

  8. undo() :撤消上次操作(如果撤销可用)

QGroupBox

  1. 去掉不需要的标题栏
    1
    QGroupBox::setStyleSheet("QGroupBox{ margin-top:0px;} QGroupBox:title {margin-top: 0px;}");

Widget(通用窗口)系列设置

通用窗口不包含菜单栏、工具栏!

侧边栏修改

特定对口修改

对于文字颜色或者背景色背景图片填充的修改建议直接在相应参数的styleSheet中修改

注意这个修改需要选中后每一条进行修改,比方说菜单栏,每一个菜单栏的下属栏等等。
示例如下:

  1. color,background-color等等直接用
  2. 贴图:
    1
    2
    3
    4
    background-image:url(:/Img/main_bgimg.jpg);
    #直接给背景贴图
    MainWindow{background-image:url(:/Img/main_bgimg.jpg);}
    #直接给背景贴图
  • 值得注意的是,如果较大类中设置了背景,建议使用#name来限定插图范围,不然大类里边的小组件样式表是不起作用的

TabWidget Setting

  • 修改表头CurrentTabText
  • 修改是否可选:setTabEnabled(int index, bool enable)

QlineEdit

  • .setPlaceholderText(""):提示输入内容

泛泛修改

只改动MainWindow即可
注意所有对样式表的修改直接在他给的条目里选择就可以,不需要手动输入标签,只要把类写出来就行
QMainWindow主窗口
QLabel纯文字

1
2
3
4
5
6
7
{
color:
#修改字体颜色
background-color:
#背景颜色

}

特殊修改

QCombobox

外观

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*QCombobox主体*/
QComboBox {
border: 2px solid #f3f3f3;/*设置线宽*/
background-color: rgb(237, 242, 255);/*背景颜色*/
border-radius: 15px;/*圆角*/
padding: 1px 2px 1px 2px; /*针对于组合框中的文本内容*/
text-align:bottom;
min-width: 9em; /*# 组合框的最小宽度*/
/*min-height: 5em;*/

border-style:solid;/*边框为实线型*/
border-width:2px;/*边框宽度*/
border-color:rgb(77, 123, 255);/*边框颜色*/

padding-left: 10px;/*左侧边距*/
}
/*QCombobox右侧按钮*/
QComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;/*放于右方顶部*/
width: 50px;/*设置按钮范围宽度*/
/*border-radius: 15px;
border-left-width: 1px;
border-left-color: darkgray;
border-left-style: solid;*/

border-top-right-radius: 3px;/*设置边框圆角*/
border-bottom-right-radius: 3px;
/*padding-right: 50px;*/
}
/*QCombobox右侧按钮的箭头图标*/
QComboBox::down-arrow {
border-image: url(:/image/down_list.png);/*自定义图片填充*/
width: 10px;/*设置该图标的宽高*/
height: 10px;
}

#程序在运行过程中,动态对QCombobox填充可选项。比如登录时填充人名数据、选择摄像头分辨率时的分辨率列表等等不固定的元素。

/* 下拉后,整个下拉窗体样式 */
QComboBox QAbstractItemView {
border: 2px solid #f3f3f3;/*边框宽度、线形、颜色*/
background-color: rgba(237, 242, 255, 1);/*背景颜色*/
border-radius: 15px;/*圆角*/
padding: 1px 2px 1px 2px; /*针对于组合框中的文本内容*/
min-width: 9em; /*# 组合框的最小宽度*/
}

/* 下拉后,整个下拉窗体每项的样式 */
QComboBox QAbstractItemView::item {
border-radius: 15px;/*圆角*/
height: 30px; /* 项的高度(设置pComboBox->setView(new QListView());后,该项才起作用) */
background-color: rgb(237, 242, 255);

}

/*以下部分不知为何不生效,有待调试*/
/* 下拉后,整个下拉窗体越过每项的样式 */
QComboBox QAbstractItemView::item:hover {
color: #FFFFF0;
/* 整个下拉窗体越过每项的背景色 */
background-color: rgb(98, 0, 255);
}

/* 下拉后,整个下拉窗体被选择的每项的样式 */
QComboBox QAbstractItemView::item:selected {
color: #FFFFF0;
background-color: rgb(0, 85, 200);
}


//填充下拉选项
ui->comboBox->clear();//清空combobox
QStandardItemModel *pItemModel = qobject_cast<QStandardItemModel*>(ui->comboBox->model());

//字体设置
int combobox_item_fontsize = 9;
QFont font;
//font.setPixelSize(combobox_item_fontsize*scale);
font.setPointSize(combobox_item_fontsize);
font.setFamily("黑体");

//填充默认项(在没有任何数据时,可以先做一个默认的提示项给用户,然后让用户自己输入)
QString tip_string(u8"请选择用户名");
ui->comboBox->addItem(tip_string);
pItemModel->item(0)->setIcon(QIcon(":/image/account.png")); //修改某项图标
pItemModel->item(0)->setForeground(QColor(255, 0, 0)); //修改某项文本颜色
pItemModel->item(0)->setBackground(QColor(220,220,220)); //修改某项背景颜色
pItemModel->item(0)->setFont(font);
pItemModel->item(0)->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter); //修改某项文本对齐方式

//填充正式项
if(ui->comboBox->currentText() == tip_string)
ui->comboBox->clear();

int i= 0;
QStringList m_list;//随便来点填充数据
m_list<<"AAA"<<"BBB"<<"CCC"<<"DDD";
foreach (QString name, m_list)
{
qDebug()<<"combobox additem:"<<name;
ui->comboBox->addItem(name);

pItemModel->item(i)->setIcon(QIcon(":/image/account.png")); //修改某项图标
//pItemModel->item(i)->setText("修改的文本 " + QString::number(i + 1)); //修改某项文本
//pItemModel->item(i)->setForeground(QColor(255, 0, 0)); //修改某项文本颜色
//pItemModel->item(i)->setBackground(QColor(220,220,220)); //修改某项背景颜色(若样式表中已经设置了表项的背景颜色,则不会生效)
pItemModel->item(i)->setFont(font);
pItemModel->item(i)->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter); //修改某项文本对齐方式
i++;
}

//以上设置完,会默认选择第一项。可以手动选择-1项,即为未选择状态
//ui->comboBox->setCurrentIndex(-1);

常用页操作

StakedWight实现点击按钮更换界面布局

  1. 在containers中找到stacked Widget(注意不是建立新的界面)并拖动成自己需要的大小
  2. 对界面进行编辑,需要的buttons拖动到上边便会成为其子部件
  3. pyuid生成代码
  4. 添加连接函数,注意menu用triggered button用clicked
    1
    2
    3
    4
    5
    6
    #添加换页槽函数
    self.actionRadiomic.triggered.connect(self.display1)

    def display1(self):
    self.stackedWidget.setCurrentIndex(1)
    #注意页码子控件一般是从0开始,1为第二页,默认初始显示0

窗口与窗口调用

子窗口的调用

子窗口部件无反应问题

笨人已经搞到崩溃,等查到debug方式的时候整个人已经萎了喝喝,父窗口改成WindowModel就可以了
Img
这是模式窗口属性,有三个值:NoModal指没有模式,也就是不会阻塞其他窗口应用的模式;WindowModal就是单窗口层次模式,只准其本身与其子窗口可以使用;ApplicationModal就是应用模式,除了本身其他窗口都不能使用。

PyQt5的QSS美化

样式表调用

样式表写出,但不显示

前边的限制用#name加括号,限定对象,在多页切换的工具中要单选出改页面(建议在右侧的工具栏中选择并进行修改)

qss调用

主函数 全局调用

1
2
3
4
5
styleFile = './pyqt5/qss/style.qss'
with open(styleFile, "r") as f:
style = f.read()
win.setStyleSheet(style)
f.close()

设置图片样式表

但是你一定要知道图片要用png格式,只有你能显示出来那个缩略图才说明成功了
border-image:url(D:/www/xxx/Pytorch/Braintumor/PyQt_test/Img/Hydrogen.jpg);

一些主题

一个很简单的有重定义边框的主题

好看的字体加颜色

  1. 深蓝色+柔和体(松散衡水体感觉是)
    1
    2
    color: rgb(44, 62, 80);
    font: italic 11pt "Cascadia Code SemiLight";
  2. 适合做题目的可爱点字体
    1
    font: 15pt "Cooper Black";
  3. 圆润的正常体
    1
    font: 9pt "Arial Rounded MT Bold";

qtawesome使用总结

  1. 展示图标库
    1

对某个控件的所有子控件进行集体美化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
self.left_widget.setStyleSheet('''
QPushButton{border:none;color:white;padding-left:5px;
height:35px;
font-size:15px;
padding-right:10px;}
QPushButton#left_label{
border:none;
border-bottom:1px solid white;
font-size:20px;
font-weight:700;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

QWidget#left_widget{
background:Gray;

border-top:1px solid white;
border-bottom:1px solid white;
border-left:1px solid white;
border-top-left-radius:10px;
border-bottom-left-radius:10px;
}
QPushButton#left_button:hover{ color:white;
border:2px solid #F3F3F5;
border-radius:15px;
background:black;}
''')

QTabWidget样式

各个组件的实际指向

QTabWidget

  1. QTabWidget显示区域的属性设置
    要在大控件里添加
    1
    2
    3
    4
    5
    6
    7
    8
    QTabWidget::pane {
    border-top: 1px solid #E5E5E5;
    border-left:1px solid #E5E5E5;
    position: absolute;
    font-size: 14px;
    background-color:#FFFFFF;
    top:-1px #稍微遮一点点选项卡的上部
    }
  2. QTabWidget 选择项的属性设置
    自己的代码~一定要注意伪状态后边是没有空格的!要不然没反应
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    QTabBar::tab{
    background-color: rgb(41, 128, 182);
    min-width:250px;
    min-height:70px;
    border-top-left-radius: 15px;
    border-top-right-radius: 15px;
    color: rgb(44, 62, 80);
    font: italic 11pt "Cascadia Code SemiLight";
    }
    QTabBar::tab:hover{
    background-color: rgb(137, 195, 235);
    border-right:2px solid;
    border-top:2px solid;
    }
    别人的代码(我就加注释啦)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    QTabBar::tab{
    background-color: rgb(41, 128, 182);
    min-width:250px;#宽度
    min-height:70px;#高度
    border-top-left-radius: 15px;
    border-top-right-radius: 15px;
    color: rgb(44, 62, 80);
    font: italic 11pt "Cascadia Code SemiLight";
    }
    QTabBar::tab:hover{
    background-color: rgb(137, 195, 235);
    border-right:2px solid; #如果要设置颜色的话在solid之后加一个#颜色
    border-top:2px solid;
    }
    只有出现TabBar才会设置标签项的颜色
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    QTabBar::tab {
    border: none;
    border-bottom-color: #FFFFFF; /* same as the pane color */
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
    min-width: 8ex;
    padding: 2px;
    font-size: 14px;
    background-color:#FFFFFF;
    }
    QTabBar::tab:selected, QTabBar::tab:hover {
    background-color:#FFFFFF;//选中背景色
    }
    QTabBar::tab:selected {
    border:none; #去掉边框
    color:#2080F7;//选中颜色
    border-bottom: 2px solid #2080F7;
    font-weight:bold;
    background-color:#FFFFFF;
    }
  3. QTabWidget 头部属性设置
    1
    2
    3
    4
    5
    6
    7
    8
    QTabWidget::tab-bar {
    border-top: 2px solid #E5E5E5;
    border-bottom: 2px solid #E5E5E5;
    border-left:1px solid #E5E5E5;
    alignment: center;//居中显示
    font-size: 14px;
    background-color:#FFFFFF;
    }
  4. 头部选项卡QTabBar::tab
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    QTabBar::tab {
    background: #051322;
    color:#7F8997;
    border: 2px;
    border-top-left-radius: 10px;
    border-top-right-radius: 10px;
    min-width: 200px;
    min-height: 35px;
    padding: 2px;
    }
    QTabBar::tab:selected{
    background-color: white;
    color:#001330;
    }
    QTabBar::tab:first{
    min-width: 35px;
    background-color: #2489F2;
    border-top-left-radius: 3px;
    border-top-right-radius: 3px;
    background-image: url(:/Resources/image/homepage.png);
    background-position: center;
    background-repeat: no-repeat;
    }
    QTabBar::tab:first:hover{
    min-width: 35px;
    background-color: #2489F2;
    border-top-left-radius: 3px;
    border-top-right-radius: 3px;
    background-image: url(:/Resources/image/homepage_hover.png);
    background-position: center;
    background-repeat: no-repeat;
    }
    QTabBar::close-button{
    border-image: url(:/Resources/image/close.png);
    }
    QTabBar::close-button:hover{
    border-image: url(:/Resources/image/close_hover.png);
    }
  5. 内容区美化
    1
    2
    3
    4
    5
    6
    7
    8
    //设置内容区域边框
    QTabWidget::pane{
    border:none;
    }
    //标题栏左侧间距
    QTabWidget::tab-bar {
    left: 1px;
    }
  6. 头部属性设置
    1
    2
    3
    4
    5
    6
    7
    8
    QTabWidget::tab-bar {
    border-top: 2px solid #E5E5E5;
    border-bottom: 2px solid #E5E5E5;
    border-left:1px solid #E5E5E5;
    alignment: center;
    font-size: 14px;
    background-color:#FFFFFF;
    }
  7. 图片插入的一个小例子
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #最左边标签未选中时显示(因为需要左边打圆角,所以和其他背景图片不一样,注意字体颜色)
    QTabBar::tab:first:!selected {
    color:#000000;
    border-image: url(:/common/images/common/左_normal.png);
    }
    #最左边标签被选中
    QTabBar::tab:first:selected {
    color:#FFFFFF;
    border-image: url(:/common/images/common/左_pressed.png);
    }
    #最右边标签未选中时显示(因为需要右边打圆角,所以和其他背景图片不一样)
    QTabBar::tab:last:!selected {
    color:#000000;
    border-image: url(:/common/images/common/右_normal.png);
    }
    #最右边标签被选中
    QTabBar::tab:last:selected {
    color:#FFFFFF;
    border-image: url(:/common/images/common/右_pressed.png);
    }
    #中间的标签未被选择的显示
    QTabBar::tab:!selected {
    color:#000000;
    border-image: url(:/common/images/common/中_normal.png);
    }
    #中间标签选中显示的图片
    QTabBar::tab:selected {
    color:#FFFFFF;
    border-image: url(:/common/images/common/中_pressed.png);
    }
  8. 改变左右滑动的按钮
    1
    2
    3
    4
    5
    6
    7
    8
    9
    QTabBar QToolButton {
    border: none;
    color: rgb(255, 206, 6);
    background-color: #0b0e11;
    }

    QTabBar QToolButton:hover {
    background-color: rgb(44, 62, 80) ;
    }

利用QProxyStyle改变TabBar位置并改变文字方向

但是是C语言()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <QPainter>
#include <QProxyStyle>

class CustomTabStyle : public QProxyStyle
{
public:
QSize sizeFromContents(ContentsType type, const QStyleOption *option,
const QSize &size, const QWidget *widget) const
{
QSize s = QProxyStyle::sizeFromContents(type, option, size, widget);
if (type == QStyle::CT_TabBarTab) {
s.transpose();
s.rwidth() = 90; // 设置每个tabBar中item的大小
s.rheight() = 44;
}
return s;
}

void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
//设置lab
if (element == CE_TabBarTabLabel) {
if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
QRect allRect = tab->rect;
//选中状态
if (tab->state & QStyle::State_Selected) {
painter->save();
painter->setPen(0xffffff);
painter->setBrush(QBrush(0xffffff));
//painter->drawRect(allRect.adjusted(6, 6, -6, -6));
painter->drawRect(allRect.adjusted(0, 0, 0, 0));
painter->restore();
}
//hover状态 鼠标移动状态
else if (tab->state & QStyle::State_MouseOver) {
painter->save();
painter->setPen(0xECECEC);//画框
painter->setBrush(QBrush(0xECECEC));
painter->drawRect(allRect.adjusted(0, 0, 0, 0));
painter->restore();
} else {
painter->setPen(0x33CCFF);
}
//字体
QTextOption option;
option.setAlignment(Qt::AlignCenter);
painter->setFont(QFont("楷体", 12, QFont::Bold));
painter->setPen(0x0A0A0A);
painter->drawText(allRect, tab->text, option);
return;
}
}
if (element == CE_TabBarTab) {
QProxyStyle::drawControl(element, option, painter, widget);
}
}
};

调用:

1
2
ui->tabWidget->setTabPosition(QTabWidget::West);
ui->tabWidget->tabBar()->setStyle(new CustomTabStyle);

自己做的好看的成品嘿嘿

  1. 蓝色的选项卡
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    QTabBar::tab{
    background-color: rgb(41, 128, 182);
    min-width:250px;
    min-height:70px;
    color: rgb(44, 62, 80);
    font: italic 11pt "Cascadia Code SemiLight";
    }
    QTabBar::tab:hover,QTabBar::tab:selected{
    border:5px solid #bce2e8;
    background-color: rgb(137, 195, 235);
    border-right:2px solid;
    border-top:2px solid;
    }

QFrame制作无边框窗口

1
2
3
4
5
6
7
#frame
{
background-color:rgb(39, 146, 195);
border: 5px, white;
border-radius: 20px;
margin: 5px;
}

QGruopBox

1
2
3
4
5
6
7
8
9
10
QGroupBox{
border:2px solid gray;
border-radius:4px;
margin-top:0.5em;
}
QGroupBox::title{
subcontrol-origin:margin;
background-color:white;
padding:0 3px;
}

Scroll Area

设置背景图片要设定里边那个Widget的样式,要不然所有子控件都会被改

1
2
3
4
5
6
#scrollAreaWidgetContents{
border-image: url(./Img/5deepblue.png);
}
QScrollArea{
border:none;
}

QComboBox

指定下拉箭头图片自定义,图片文件为name.png

  1. 所有的下拉箭头
    1
    QConboBox:drop-down{image:url(name.png)}
  2. 指定ID的下拉箭头
    1
    QComboBox#myQComboBox::drop-down {image:url(dropdown.png)}

QSS伪状态

QSS的伪状态选择器是一个以冒号开头的选择表达式,限制控件在某种状态时才可以使用QSS规则,只能描述某一个控件或者一个复合控件的自控件的状态,只能放在选择器的最后边。

hover:鼠标指针经过的状态

1
2
3
4
5
6
QComboBox:hover{background-color:red}
#经过combobox时其背景变为红色
QComboBox::drop-down:hover{background-color:red}
#经过其下拉箭头的时候,下拉箭头的背景变成12
QCheckBox:hover:checked{color:white}
#多种伪状态可以同时使用:表示当鼠标指针经过一个选中的QCheckBox时,设置其文字的前景色为白色12

:!hove可表示没有鼠标经过的状态
实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import sys
from PyQt5.QtWidgets import *

class WindowDemo(QWidget):
def __init__(self):
super(WindowDemo, self).__init__()
self.initUI()
def initUI(self):
#实例化列表控件
combo=QComboBox(self)
#设置列表控件的名称
combo.setObjectName('myQComboBox')

#添加条目到列表控件
combo.addItem('Window')
combo.addItem('Ubuntu')
combo.addItem('Red Hat')
#控件移动到指定位置
combo.move(50,50)
#设置窗口的标题与初始窗口的属性
self.setGeometry(250,200,320,150)
self.setWindowTitle('QComboBox样式')

#设置样式

qssStyle='''
QComboBox#myQComboBox::drop-down{
image:url(./images/dropdown.png)
}
QComboBox#myQComboBox::drop-down:hover{
background-color:red
}
'''
self.setStyleSheet(qssStyle)
if __name__ == '__main__':
app=QApplication(sys.argv)
win=WindowDemo()
win.show()
sys.exit(app.exec_())

MainWindow:直接在retranslateUi后边设置

1
2
3
4
5
6
7
8
9
10
11
#MainWindow.setWindowOpacity(0.9) # 设置窗口透明度
MainWindow.setAttribute(QtCore.Qt.WA_TranslucentBackground) # 设置窗口背景透明
#MainWindow.setWindowFlag(QtCore.Qt.FramelessWindowHint) # 隐藏边框
pe = QPalette()
MainWindow.setAutoFillBackground(True)
pe.setColor(QPalette.Window,Qt.lightGray) #设置背景色
#pe.setColor(QPalette.Background,Qt.blue)
MainWindow.setPalette(pe)

Ui_MainWindow3.setWindowTitle("语音识别")
Ui_MainWindow3.setWindowIcon(QIcon('Amg.jpg')) #设置图标,可以用图片,也可以用qtawesome

PushButton:直接在初始setup后设置

  1. 设置按钮前的图标
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    spin_icon = qtawesome.icon('fa5s.microphone-alt', color='white')
    #图标名参考示例格式,以及qtawesome的readme中对于库的提示来写,格式就是 库.图标名
    self.pushButton.setIcon(spin_icon)#设置图标
    self.pushButton.setIconSize(QtCore.QSize(50,50))#需要导入库from PyQt5.QtCore import QSize
    self.pushButton.setStyleSheet('''QPushButton{background-color: rgb(70, 70, 70);
    color: rgb(255, 255, 255);}
    QPushButton:hover{color:white;
    border:2px solid #F3F3F5;
    border-radius:35px;
    background:darkGray;}''')
  2. 图标+圆角(图标用的是flaticon)
    在qss文件添加以下内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    /*设置控件的背景*/
    QTabWidget
    {
    background-color:rgb(104,191,249);
    }
    /*设置控件下面板的背景颜色*/
    QTabWidget::pane
    {
    background-color: rgb(228, 233, 242);
    border:none;

    }
    /*设置控件下选择页的颜色*/
    QTabBar::tab
    {
    font: 15pt "Chinese fine black";
    background-color:rgb(104,191,249);
    min-width: 60px;
    min-height: 30px;
    padding: 2px;
    }
    /*设置控件下选择页被选中的颜色*/
    QTabBar::tab:selected
    {
    background-color: rgb(228, 233, 242);
    }

将最大化最小化按钮设置为像苹果的三个点点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class gui_view(QWidget):
def __init__(self):
super(gui_view, self).__init__()

self.resize(500, 350)
self.setWindowFlags(Qt.FramelessWindowHint) # 去边框
# # self.setAttribute(Qt.WA_TranslucentBackground) # 设置窗口背景透明

button_red = QPushButton(self)
button_red.move(20, 20)
button_red.setFixedSize(20, 20)
button_red.setStyleSheet("QPushButton{\n"
" background:#CE0000;\n"
" color:white;\n"
" box-shadow: 1px 1px 3px;border-radius: 10px;\n"
"}\n"
"QPushButton:hover{ \n"
" background:red;\n"
"}\n"
"QPushButton:pressed{\n"
" border: 1px solid #3C3C3C!important;\n"
" background:black;\n"
"}")
button_red.clicked.connect(self.quit_button)

button_orange = QPushButton(self)
button_orange.move(50, 20)
button_orange.setFixedSize(20, 20)
button_orange.setStyleSheet("QPushButton{\n"
" background:orange;\n"
" color:white;\n"
" box-shadow: 1px 1px 3px;border-radius: 10px;\n"
"}\n"
"QPushButton:hover{ \n"
" background:yellow;\n"
"}\n"
"QPushButton:pressed{\n"
" border: 1px solid #3C3C3C!important;\n"
" background:black;\n"
"}")

button_green = QPushButton(self)
button_green.move(80, 20)
button_green.setFixedSize(20, 20)
button_green.setStyleSheet("QPushButton{\n"
" background:green;\n"
" color:white;\n"
" box-shadow: 1px 1px 3px;border-radius: 10px;\n"
"}\n"
"QPushButton:hover{ \n"
" background:#08BF14;\n"
"}\n"
"QPushButton:pressed{\n"
" border: 1px solid #3C3C3C!important;\n"
" background:black;\n"
"}")

def quit_button(self):
quit()

最小化

1
2
3
4
5
6
7
8
9
10
11
QPushButton{
background:#6C6C6C;
color:white;
box-shadow: 1px 1px 3px rgba(0,0,0,0.3);font-size:16px;border-radius: 8px;font-family: 微软雅黑;
}
QPushButton:hover{
background:#9D9D9D;
}
QPushButton:pressed{
border: 1px solid #3C3C3C!important;
}

关闭

1
2
3
4
5
6
7
8
9
10
11
12
QPushButton{
background:#CE0000;
color:white;
box-shadow: 1px 1px 3px rgba(0,0,0,0.3);font-size:16px;border-radius: 8px;font-family: 微软雅黑;
}
QPushButton:hover{
background:#FF2D2D;
}
QPushButton:pressed{
border: 1px solid #3C3C3C!important;
background:#AE0000;
}

少许动画操作

静态图片+动态文字

1
2
3
4
5
6
7
8
9
10
11
12
13
app = QApplication(sys.argv)
pixmap = QPixmap("Img/brain1.jpeg")
splash = QSplashScreen(pixmap)
splash.show()
#splash.setCursor(PyQt5.QtWidgets.BlankCursor) # 设置点击图标关闭事件
splash.showMessage("加载中") # 第二个参数为字的位置,第三个参数为颜色
app.processEvents() # 使程序还能响应其他事件

dialog = Ui_MainDialog() #先创建好实例后取消
splash.finish(dialog) # main为主界面的实例

dailog.exec()
sys.exit(app.exec_())

动态图gif

使用label实现:

1
2
3
self.gif = QMovie('bg2.gif')
self.label.setMovie(self.gif)
self.gif.start()

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
app = QApplication(sys.argv)
label = QLabel("")
mv = QMovie(":/gif/loading.gif")
label.setMovie(mv) #其实是把label当成了一个载体,把mv放到label上
label.setWindowFlags(Qt.FramelessWindowHint) # label窗口无边框设置
label.setAttribute(Qt.WA_TranslucentBackground) # label背景透明
label.move((app.desktop()->width() - window.width()) / 2, (app.desktop()->height() - window.height()) / 2) # 调整位置
label.setScaledContents(True)
mv.start()
label.show()
main = None #把main改成你的界面名字
while True:
app.processEvents() # 使动画正常播放,不影响主界面构造
if not main:
main = MainWinodw() #实例创建你应该有的实例
break
main.show()
label.close()
sys.exit(app.exec_())

源代码

class_test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import sys
import PyQt5
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import *

from dictest import Ui_MainDialog

if __name__ == "__main__":
app = QApplication(sys.argv)

label = QLabel("")
mv = QMovie("./Img/11.jpeg")
label.setMovie(mv)
label.setWindowFlags(Qt.FramelessWindowHint) # label窗口无边框设置
label.setScaledContents(True)
mv.start()
label.show() #这句一定要有啊。。uic加载的时候也需要show

dailog = None
while True:
app.processEvents() # 使动画正常播放,不影响主界面构造
if not dailog:
dailog = Ui_MainDialog()
break
label.close()
dailog.exec()

sys.exit(app.exec_())

showImage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 定义MyFigure类的一个实例

self.F = MyFigure(width=5, height=4, dpi=100)
# 在GUI的groupBox中创建一个布局,用于添加MyFigure类的实例(即图形)后其他部件。
self.gridlayout_1 = QGridLayout(self.graphicsView_2) # 继承容器
self.gridlayout_1.addWidget(self.F, 0, 1)
#self.showimage()

self.F2 = MyFigure(width=5, height=4, dpi=100)
self.gridlayout_2 = QGridLayout(self.graphicsView_3) # 继承容器
self.gridlayout_2.addWidget(self.F2, 0, 1)

self.F3 = MyFigure(width=5, height=4, dpi=100)
self.gridlayout_3 = QGridLayout(self.graphicsView_4) # 继承容器
self.gridlayout_3.addWidget(self.F3, 0, 1)

# self.horizontalSlider.valueChanged.connect(self.bindSlider)
self.showimage()
# SimpleView.VisNii(self,self.ori_path)

VTK(Vedo)实现

可以进行3D可视化,显示感兴趣区域,2D切割,使用的mySlicer是在vedo3dslicer上修改的,只是三个切片分别分装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import pandas as pd
from PyQt5 import Qt
from PyQt5.QtWidgets import QGridLayout, QDialog, QGraphicsDropShadowEffect
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import *
from qtpy import uic

import matplotlib
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import itk
from vedo import *
from vedo import Volume, Plotter
#用来做数据处理

matplotlib.use("Qt5Agg") # 声明使用QT5

class child(QDialog):
def __init__(self,path,ori_path,label_path):
super().__init__()
self.openingUI = uic.loadUi("visualization.ui", self)
self.read(path,ori_path,label_path)
def read(self,path,ori_path,label_path):
readData.read_csv(self.openingUI,path,ori_path,label_path)

class readData:
def __init__(self):
pass
def is_number(self,s):
try:
float(s)
return True
except ValueError:
pass
try:
import unicodedata
unicodedata.numeric(s)
return True
except (TypeError, ValueError):
pass
return False
def read_csv(self,path,ori_path,label_path):
df = pd.read_csv(path)
numdata = pd.DataFrame() #数字特征
for idx, row in df.iterrows(): # 遍历 DataFrame
if readData.is_number(self,row['Value']):#存储数字结果
numdata = numdata.append({'Feature': row['Feature'], 'Value': row['Value']}, ignore_index=True)
#for idx, row in numdata.iterrows(): # 遍历 特征文件并提取所有的有数字特征
#print(idx, row['Value'])
DataProsess.visData(self,numdata,ori_path,label_path)

class DataProsess:
def __init__(self):
pass
def visData(self,numdata,ori_path,label_path):
self.ori_path = ori_path
self.label_path = label_path
self.numdata = numdata

self.vtkWidget = QVTKRenderWindowInteractor()
self.gridlayout = QGridLayout(self.graphicsView_2)
self.gridlayout.addWidget(self.vtkWidget)

self.vtkWidget_2 = QVTKRenderWindowInteractor()
self.gridlayout_2 = QGridLayout(self.graphicsView_3)
self.gridlayout_2.addWidget(self.vtkWidget_2)

Vis_3D.readData(self,ori_path,label_path)

class Vis_3D(QDialog):
def __init__(self):
pass

def readData(self,ori_path,label_path):
normal = [0, 0, 1]
self.cmap = "gist_stern_r"
self.cmap2 = "viridis_r"

def func(w, _):
c, n = self.pcutter.origin, self.pcutter.normal
vslice = self.vol.slice_plane(c, n, autocrop=True, border=1.0).cmap('bone') # 给动态的切割面一个原点和一条法线
vslice.name = "Slice"
vslice2 = self.vol2.slice_plane(c, n, autocrop=True, border=1.0).cmap(self.cmap2) # 给动态的切割面一个原点和一条法线
vslice2.name = "Slice2"
self.plt2.show(vslice, __doc__) # 少加点就行了...

itk_img = itk.imread(filename=ori_path)
vtk_img = itk.vtk_image_from_image(l_image=itk_img)
self.vol = Volume(vtk_img).cmap("gist_stern_r")
itk_img = itk.imread(filename=label_path)
vtk_img = itk.vtk_image_from_image(l_image=itk_img)
self.vol2 = Volume(vtk_img).cmap("viridis_r")

vslice = self.vol.slice_plane(self.vol.center(), normal).cmap("bone")
vslice.name = "Slice"
vslice2 = self.vol2.slice_plane(self.vol2.center(), normal).cmap("bone")
vslice.name = "Slice2"

self.plt = Plotter(axes=8, N=1, bg="k", bg2="bb", interactive=True,qt_widget=self.vtkWidget) # N:desired renderers,可以qt_windows,sharecam,
self.plt.interactive()
self.plt.show(self.vol,self.vol2, __doc__, zoom=1.5)#

self.plt2 = Plotter(axes=8, N=1, bg="k", bg2="bb", interactive=True, qt_widget=self.vtkWidget_2)
#右侧的切面模型
self.pcutter = PlaneCutter(
vslice,
normal=normal, # 平面的法线,此处赋值为平面法线
alpha=0, # 输入网络截止部分的透明度
c=(0.25, 0.25, 0.25),
padding=0,
can_translate=True,
can_scale=True, # 启用部件的缩放功能
)
self.pcutter.add_observer("interaction", func)
self.plt2.at(0).add(self.pcutter)
self.plt2.interactive()
self.plt2.show(vslice, __doc__) # 少加点就行了...

self.arr = vslice.pointdata[0] # retrieve vertex array data

def flatFunc(evt):
if not evt.actor:
return
try:
pid = evt.actor.closest_point(evt.picked3d, return_point_id=True)
except AttributeError:
pass
else:
txt = f"Position:{precision(evt.actor.picked3d, 3)}\noriginal_shape_MeshVolume = {self.arr[pid]}"

pts = evt.actor.points()
sph = Sphere(pts[pid], c='orange7').pickable(True)
fp = sph.flagpole(txt, s=7, offset=(-150, 15), font=2).follow_camera()
# remove old and add the two new objects
self.plt.remove('Sphere', 'FlagPole').add(sph, fp).render()

self.arr = vslice.pointdata[0] # retrieve vertex array data

def flatFunc_3D(evt):
if not evt.actor:
return
try:
pid = evt.actor.closest_point(evt.picked2d, return_point_id=True)
except AttributeError:
pass
else:
for idx,row in self.numdata[:5].iterrows():
txt = idx['Feature'] + f"idx:{row['Value']}\noriginal_shape_MeshVolume = {self.arr[pid]}"
pts = evt.actor.vertics()
sph = Sphere(pts[pid], c='orange7').pickable(True)
fp = sph.flagpole(txt, s=7, offset=(-150, 15), font=2).follow_camera()
# remove old and add the two new objects
self.plt2.remove('Sphere', 'FlagPole').add(sph, fp).render()

self.plt.add_callback('as my mouse moves please call', flatFunc) # be kind to vedo ;)
self.plt2.add_callback('MouseClick', flatFunc_3D)

New_UI单模态的可视化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
import nibabel
from PyQt5 import uic
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QDialog, QGraphicsDropShadowEffect, QMainWindow, QFileDialog, QGridLayout

import nibabel as nib
from pathlib import Path

from RadiomicsPage import RadiomicsFunc
from VTK_new import SimpleView

#import HeterogeneityCAD
from DlpFeatures import DLPExtractor
#from RadioMLPage import RadioML

import matplotlib

matplotlib.use("Qt5Agg") # 声明使用QT5

class Ui_MainDialog(QDialog):
def __init__(self):
super().__init__()
self.dia = uic.loadUi("New_UI.ui", self)
self.initDia()

def initDia(self):
self.dia.textBrowser_2.append(
"Extracmator is a free open source software platform for medical image progressing and 3D visualization of image data."
"This mudule contains some basic methods to visualize and give anlaysis of medical image computing data sets.\n")
self.dia.textBrowser_3.append(
"You can perform basic traditional radiomics analysis by doing this:\n"
"1.Click 'Input Label' and select your ROI\n"
"2.Click 'Input Image' and select your brain image\n"
"3.Choose Pyradiomics and select the settings you need. If you're not sure about that, commonSettings and choosing your output table path will be fine.\n"
"4.You can name the output csv.\n"
"5.Click the 'Apply' to see the visual analysis.If you're satisfied with that, click 'Save Data' to save it.")

# 设置页面样式
self.dia.setWindowFlag(Qt.FramelessWindowHint) # 将界面设置为无框
self.dia.setAttribute(Qt.WA_TranslucentBackground) # 将界面属性设置为半透明
self.dia.shadow = QGraphicsDropShadowEffect() # 设定一个阴影,半径为10,颜色为#444444,定位为0,0
self.dia.shadow.setBlurRadius(10)
self.dia.shadow.setColor(QColor("#444444"))
self.dia.shadow.setOffset(0, 0)
self.dia.frame.setGraphicsEffect(self.dia.shadow) # 为frame设定阴影效果
self.dia.progressBar = self.progressBar
self.progressBar.setMinimum(0)# 将进度条最小值设为0

self.pushButton_close.clicked.connect(self.quit_button)
self.dia.listWidget.itemClicked.connect(self.ChangePage)
self.dia.listWidget_2.itemClicked.connect(self.ChangePage)

self.Actions()

def Actions(self):
# 初始化属性
self.ori_path = ''
# 上传图片
self.data2 =''
self.dia.pushButton.clicked.connect(self.bindButton)
self.dia.pushButton_ori.clicked.connect(lambda: self.oriImg()) # 上传图像
self.RadiomicsPart()
self.DLPart()

def getImage(self):
try:
data_nii = nib.load(self.ori_path)
except FileNotFoundError:
pass
else:
data = data_nii.get_fdata()
return data

# 获取并展示全图
def oriImg(self):
file_name = QFileDialog.getOpenFileName(None, "Open File", "./", "nii(*.nii.gz;*.nii;*.nrrd)")
try:
self.ori_path = file_name[0] # 提取文件路径
except nibabel.filebasedimages.ImageFileError:
pass
else:
SimpleView.VisNii(self, self.nii_path, self.ori_path)
self.showimage()

# 展示切片
def showimage(self):
self.data = self.getImage()
data1 = self.data

def RadiomicsPart(self):
# radiomics函数调用
self.nii_path = ' '
self.RadiomicsFunc = RadiomicsFunc()

self.dia.radioButton_Para_Cus.clicked.connect(lambda: self.RadiomicsFunc.cusParaSet(self.lineEdit_fileName_2))

if self.dia.groupBox_2.isChecked:
self.RadiomicsFunc.setupParaFile()
self.dia.comboBox_ImgType.currentIndexChanged.connect(lambda: self.Change(
self.comboBox_ImgType.currentIndex()))#检查是否为第一项,如果如果是,允许选择特征
self.dia.comboBox_ImgType.currentIndexChanged.connect(lambda: self.RadiomicsFunc.ImgType_init(
self.comboBox_ImgType.currentIndex())) # currentIndex返回当前项的序号(int),第一个项的序号为0

self.dia.comboBox_ImgType.currentIndexChanged.connect(lambda: self.stackedWidget.setCurrentIndex(
self.comboBox_ImgType.currentIndex()-1))#对应换页

self.PycheckStatus()
self.save_name = 'Features_Result' #给特征文件一个初始的名字

self.save_path = ' '
self.dia.pushButton_4.clicked.connect(
lambda: self.getSavePath()) # 获取文件输出地址
self.dia.lineEdit_fileName.textEdited[str].connect(lambda: self.onChange1(self.dia.lineEdit_fileName)) # 实时获取文件名

self.dia.pushButton_6.clicked.connect(lambda: self.RadiomicsFunc.outPuts(self.save_path,
self.save_name,
self.nii_path,True,
self.ori_path,
self.label_path)) #保存数据
self.dia.pushButton_2.clicked.connect(lambda: self.RadiomicsFunc.outPuts(self.save_path,
self.save_name,
self.nii_path,True,
self.ori_path,
self.label_path)) #保存数据

def DLPart(self):
self.batch_path = ' '
self.dia.pushButton_8.clicked.connect(
lambda: self.getBatchPath()) # 批量导入

#选择网络类型,序号从0开始
self.dia.comboBox_NetType.currentIndexChanged.connect(lambda: self.DlpFeatures.netSet(
self.comboBox_NetType.currentIndex()))

self.DlpFeatures = DLPExtractor(self.dia.progressBar)

#选择网络

# 实时获取文件名
self.dia.lineEdit_fileName_3.textEdited[str].connect(lambda: self.onChange1(self.dia.lineEdit_fileName_3))

# 点击Apply图片转化
self.save_path = 'D:/www/xxx/Pytorch/Braintumor/PyQt_test/Result/'
self.dia.pushButton_7.clicked.connect(
lambda: self.DlpFeatures.converts(self.batch_path,self.save_path,self.dia.textBrowser,self.save_name) ) #nii_path, outputfile

"""
def RadioML(self):
self.dia.pushButton_11(lambda : RadioML.Main())
self.dia.pushButton_17(lambda: RadioML.Features())
self.dia.pushButton_18(lambda: RadioML.Score())
"""

"""
def HCADPart(self):
self.dia.pushButton_Reload.connect(self.bindButton)
self.dia.pushButton_AllNodes.connect(self.getBatchPath())
self.dia.pushButton_Remove.connect(self.delete()) #删除路径函数
self.dia.pushButton_RemoveAll(self.deleteAll()) #删除所有
self.dia.pushButton_SecROI(self.getROI()) #获取ROI
"""
def getBatchPath(self): #批量导入
batch_path = QFileDialog.getExistingDirectory(self, "choose directory", "./")
batch_path = batch_path + '/' #在这里提取了地址之后给他加一个下级,转字符,这样可以直接调用下部文件夹
self.batch_path = batch_path
print(self.batch_path)

def getSavePath(self): # 获取存储地址
self.save_path = QFileDialog.getExistingDirectory(self, "choose directory", "./")

def onChange1(self,lineEdit): #实时获取文件名
self.VAR = lineEdit.text()# 设置保存特征的csv文件名
self.save_name = self.VAR
print(self.VAR)

def Change(self,tag):
if tag == 1:
self.groupBox.setCheckable(True)

def ChangePage(self,item): #list中点击对应右侧换行
index = self.dia.listWidget.row(item)
if index==2:
self.display1()
elif index==1:
self.display2()
elif index==3:
self.display3()
elif index == 0:
self.display5()
index = self.dia.listWidget_2.row(item)
if index== 0:
self.display4()

def display1(self): # Dlp
self.stackedWidget_2.setCurrentIndex(1)

def display2(self): #Pyradiomics
self.stackedWidget_2.setCurrentIndex(2)

def display3(self):#Here
self.stackedWidget_2.setCurrentIndex(3)

def display4(self): #RadioML
self.stackedWidget_2.setCurrentIndex(4)

def display5(self): # Wel
self.stackedWidget_2.setCurrentIndex(0)

def PycheckStatus(self):
self.dia.checkBox_Nor1.stateChanged.connect(lambda: self.RadiomicsFunc.Add_Nor(self.dia.checkBox_Nor1.isChecked()))
self.dia.checkBox_Nor2.stateChanged.connect(lambda: self.RadiomicsFunc.Add_Nor(self.dia.checkBox_Nor2.isChecked()))
self.dia.checkBox_Nor3.stateChanged.connect(lambda: self.RadiomicsFunc.Add_Nor(self.dia.checkBox_Nor3.isChecked()))
self.dia.checkBox_Nor4.stateChanged.connect(lambda: self.RadiomicsFunc.Add_Nor(self.dia.checkBox_Nor4.isChecked()))
self.dia.checkBox_15.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_FirstSpe(self.dia.checkBox_15.isChecked()))
self.dia.checkBox_14.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_FirstSpe2(self.dia.checkBox_14.isChecked()))
self.dia.checkBox_12.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_NFirstSpe2(self.dia.checkBox_12.isChecked()))
self.dia.checkBox_13.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_FirstSpe2(self.dia.checkBox_13.isChecked()))
self.dia.checkBox_Mas4.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_Mask(self.dia.checkBox_Mas4.isChecked()))
self.dia.checkBox_Mas3.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_Mask(self.dia.checkBox_Mas3.isChecked()))
self.dia.checkBox_Mas2.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_Mask(self.dia.checkBox_Mas2.isChecked()))
self.dia.checkBox_Mas1.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_Mask(self.dia.checkBox_Mas1.isChecked()))
self.dia.checkBox_Mis2.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_Misc(self.dia.checkBox_Mis2.isChecked()))
self.dia.checkBox_Res1.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_Resampling(self.dia.checkBox_Res1.isChecked()))
self.dia.checkBox_Res3.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_Resampling(self.dia.checkBox_Res3.isChecked()))
self.dia.checkBox_2D1.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_2D(self.dia.checkBox_2D.isChecked()))
self.dia.checkBox_Voxel.stateChanged.connect(
lambda: self.RadiomicsFunc.Add_Voxel(self.dia.checkBox_Voxel.isChecked()))

def bindButton(self):
file_name = QFileDialog.getOpenFileName(None, "Open File", "./", "nii(*.nii.gz;*.nii;*.nrrd)")
self.nii_path = file_name[0] # 提取文件路径
try:
self.data_mask = nib.load(Path(self.nii_path))
except nibabel.filebasedimages.ImageFileError:
pass
else:
self.data2 = self.data_mask.get_fdata()
SimpleView.VisNii(self, self.nii_path, self.ori_path)

def bindSlider(self):
slice_idx = self.horizontalSlider.value()
self.showimage(slice_idx)

def quit_button(self):
quit()

def mousePressEvent(self, event): # 鼠标左键按下时获取鼠标坐标,按下右键取消
if event.button() == Qt.LeftButton:
self.m_flag = True
self.m_Position = event.globalPos() - self.pos()
event.accept()
elif event.button() == Qt.RightButton:
self.m_flag = False

def mouseMoveEvent(self, QMouseEvent): # 鼠标在按下左键的情况下移动时,根据坐标移动界面
try:
if Qt.LeftButton and self.m_flag:
self.move(QMouseEvent.globalPos() - self.m_Position)
QMouseEvent.accept()
except AttributeError:
pass

def mouseReleaseEvent(self, QMouseEvent): # 鼠标按键释放时,取消移动
self.m_flag = False

PyRadiomicsPage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
from __future__ import print_function

import sys

import pandas as pd
from PyQt5.QtCore import pyqtSignal
from radiomics import featureextractor as FEE # This module is used for interaction with pyradiomics
import yaml

from PyQt5 import uic
from PyQt5.QtWidgets import QDialog, QFileDialog, QApplication

from DataProsessing import child


class RadiomicsFunc(QDialog):
isChecked = pyqtSignal(int)
def __init__(self):
super().__init__()
self.dia = uic.loadUi("New_UI.ui", self)

#创建参数文件
def setupParaFile(self):
self.para_path = './ParaFile/Param.yml'

def ImgType_init(self,tag):
self.para_path = './ParaFile/Param.yml'
settingData = {

}
with open(self.para_path, 'w', encoding='utf-8') as f:
if tag == 1: #Original
self.displayOri()
elif tag == 2:
self.displayCT()
elif tag == 3:
self.displayMR3()
elif tag == 4:
self.displayMR5()
elif tag == 5:
self.displayMR()
yaml.dump(data=settingData, stream=f, allow_unicode=True)

def featureSet(self):#get10PercentileFeatureValue():获取百分之十的特征值
# 写入的数据类型是字典
featureData = {

}
with open(self.para_path, 'w', encoding='utf-8') as f:#如果没有选择特征计算,那么自动全选,排除不推荐的功能
if self.checkBox_First.isChecked():
featureData["featureClass"] = {"firstorder": [] }
#self.tabWidget.setTabEnabled(1)
#else:
if self.checkBox_shape.isChecked():
featureData["featureClass"] = {"shape": [] }
#self.tabWidget.setTabEnabled(2)
if self.checkBox_glcm.isChecked():
featureData["featureClass"] = {"glcm": [] }
#self.tabWidget.setTabEnabled(3)
if self.checkBox_glrlm.isChecked():
featureData["featureClass"] = {"glrlm": []}
#self.tabWidget.setTabEnabled(4)
if self.checkBox_glszm.isChecked():
featureData["featureClass"] = {"glszm": [] }
#self.tabWidget.setTabEnabled(5)
if self.checkBox_gldm.isChecked():
featureData["featureClass"] = {"gldm": [] }
#self.tabWidget.setTabEnabled(6)
if self.checkBox_ngtdm.isChecked():
featureData["featureClass"] = {"ngtdm": [] }
#self.tabWidget.setTabEnable(7)
yaml.dump(data=featureData, stream=f, allow_unicode=True)

def outPuts(self,place,name,nii_path,ifsave,ori_path,label_path):
self.ori_path = ori_path
self.label_path = label_path
# 使用配置文件初始化特征抽取器
extractor = FEE.RadiomicsFeatureExtractor(parameter_file=self.para_path)
save_path = name + '.csv'
#print(save_path)

# 运行程序,提取特征
try:
result = extractor.execute(ori_path, nii_path) # 抽取特征,第一个是原图像,第二个是ROI
except ValueError:
result = extractor.execute(ori_path, nii_path, label=2)
type(result)

# 储存数据
df = pd.DataFrame()
for key, value in result.items():
# 如果当前特征是所选特征之一,则将其添加到 DataFrame
# if key in selected_features:
df = df.append({'Feature': key, 'Value': value}, ignore_index=True)
# 清空,进行下一次遍历
ori_path = None
lab_path = None

if ifsave == True :
# 将 DataFrame 保存为 CSV 文件
df.to_csv(save_path, index=False)
RadiomicsFunc.invokeDialog_1(self, save_path)

# 界面弹出
def invokeDialog_1(self,path):
#app1 = QApplication(sys.argv)
dialog = child(path,self.ori_path,self.label_path)
#child.read(dialog,path,self.ori_path,self.label_path) #数据可视化
dialog.exec()
#sys.exit(app1.exec_())

def displayOri(self):
with open('./ParaFile/exampleCT.yaml', "rb") as ff:
content = ff.read()
with open(self.para_path, "ab") as f2:
# 将读取的数据写入到新的对象中
f2.write(content)
def displayCT(self):
with open('./ParaFile/exampleCT.yaml', "rb") as ff:
content = ff.read()
with open(self.para_path, "ab") as f2:
# 将读取的数据写入到新的对象中
f2.write(content)
def displayMR3(self):
with open('./ParaFile/exampleMR_3mm.yaml', "rb") as ff:
content = ff.read()
with open(self.para_path, "ab") as f2:
# 将读取的数据写入到新的对象中
f2.write(content)
def displayMR5(self):
with open('./ParaFile/exampleMR_5mm.yaml', "rb") as ff:
content = ff.read()
with open(self.para_path, "ab") as f2:
# 将读取的数据写入到新的对象中
f2.write(content)
def displayMR(self):
with open('./ParaFile/exampleMR_NoResampling.yaml.yaml', "rb") as ff:
content = ff.read()
with open(self.para_path, "ab") as f2:
# 将读取的数据写入到新的对象中
f2.write(content)

def Add_Nor(self, state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
# print(str(state))
if state:
data["setting"] = {"normalize": "True", "normalizeScale": 500}
yaml.dump(data=data, stream=f, allow_unicode=True)

def Add_Mask(self, state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state:
data["setting"] = {"minimumROIDimensions": "2", "minimumROISize": "50"}
yaml.dump(data=data, stream=f, allow_unicode=True)

def Add_Bin(self, state): # 修改为带有BinWidth的输入项
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state: # 可以改成binwidth的输入项
data["setting"] = {"minimumROISize": "50"}
yaml.dump(data=data, stream=f, allow_unicode=True)

def Add_Misc(self, state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state:
data["setting"] = {"label": 1}
yaml.dump(data=data, stream=f, allow_unicode=True)

def Add_FirstSpe(self, state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state:
data["setting"] = {"voxelArrayShift": "1000"}
yaml.dump(data=data, stream=f, allow_unicode=True)

def Add_FirstSpe2(self, state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state:
data["setting"] = {"voxelArrayShift": "300"}
yaml.dump(data=data, stream=f, allow_unicode=True)

def Add_Resampling(self, state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state:
data["setting"] = {"interpolator ": "'sitkBSpline'", "resampledPixelSpacing": " [2, 2, 2]"}
yaml.dump(data=data, stream=f, allow_unicode=True)

def Add_Resampling2(self, state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state:
data["setting"] = {"preCrop ": "true"}
yaml.dump(data=data, stream=f, allow_unicode=True)

"""
if self.dia.checkBox_4.isChecked():
data["setting"] = {"minimumROIDimensions": "2","minimumROISize": "50"}
elif self.dia.checkBox_5.isChecked(): #可以改成binwidth的输入项
data["setting"] = {"minimumROISize": "50"}
elif self.dia.checkBox_6.isChecked():
data["setting"] = {"voxelArrayShift": "1000"}
elif self.dia.checkBox.isChecked():#label数的输入项
data["setting"] = {"label": 1}
"""

def Add_2D(self, state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state:
data["setting"] = {"force2D ": "true"} # 获取一个dimension,输入
yaml.dump(data=data, stream=f, allow_unicode=True)

def Add_Voxel(self,state):
data = {

} # 有一个问题是会一直先输出一个空字典,还不知道怎么避免捏
with open(self.para_path, 'a', encoding='utf-8') as f:
if state:
data["voxelSetting"] = {"kernelRadius ": 2,"maskedKernel":"true","initValue":"nan","voxelBatch":10000} # 获取一个dimension,输入
yaml.dump(data=data, stream=f, allow_unicode=True)

def cusParaSet(self,place):
file_name = QFileDialog.getOpenFileName(None, "Open File", "./", "yaml(*.yml;*.yaml)")
self.para_path = file_name[0]
place.setText(self.para_path)
#读取yaml文件

"""
if tag == 1: #Original
settingData["imageType"] = {"Original": {}} #写入参数设定文件
self.displayOri(settingData)
elif tag == 2:
settingData["imageType"] = {"Original": {}}
self.displayCT(settingData)
elif tag == 3:
settingData["imageType"] = {"LoG": {}}
elif tag == 4:
settingData["imageType"] = {"Square": {}}
elif tag == 5:
settingData["imageType"] = {"SquareRoot": {}}
elif tag == 6:
settingData["imageType"] = {"Logarithm": {}}
elif tag == 7:
settingData["imageType"] = {"Exponential": {}}
elif tag == 8:
settingData["imageType"] = {"Gradient": {}}
elif tag == 9:
settingData["imageType"] = {"LocalBinaryPattern2D": {}}
else :
settingData["imageType"] = {"LocalBinaryPattern3D": {}}
"""

RadioML

直接使用python对R语言调用

``ruby
#from rpy2 import robjects

import logging
import os
import platform
from functools import lru_cache
from rpy2.situation import get_r_home, get_rlib_path

def _fix_r_home():
“””
Fix the R_HOME env var if we need to
“””
r_home = os.environ.get(“R_HOME”)
#print(r_home)

if r_home:
    os.environ["R_HOME"] = r_home.replace("\\", "/")

if not get_r_home():
    raise OSError("R_HOME is not set.")

def _add_dll_directory():
“””
Adds the platform’s R library path to the allowed library
load dirs. Stops weird DLL loading issues if you are using
R in a different path than the system install dir.

Only applicable on Windows
"""

if "add_dll_directory" not in dir(os):
    return  # not windows

r_home = get_r_home()
system = platform.system()
dll_location = get_rlib_path(r_home, system)
dll_files_location = os.path.dirname(dll_location)

if dll_files_location not in os.getenv("PATH"):
    logging.warning(f"R DLL location is not in the path: {dll_files_location}")

# fix security in recent Python versions
os.add_dll_directory(dll_files_location)

@lru_cache
def get_robjects():
“””
Fix path and DLL loading issues before loading rpy2
“””
_fix_r_home()
_add_dll_directory()

from rpy2 import robjects
from rpy2.robjects import pandas2ri
pandas2ri.activate()

return robjects

class RadioML():
def init(self):
pass
def Main(self,robjects):
robjects.r.source(‘./RadioML-main.R’)
def Features(self,robjects):
robjects.r.source(‘./RadioML_features.R’)
def Score(self,robjects):
robjects.r.source(‘./RadioML_score.R’)

new_robjects = get_robjects()
test = RadioML()
test.Main(new_robjects)
#”D:/www/xxx/Pytorch/Braintumor/PyQt_test/lib/RadioML-main/RadioML_main.R”

1
2
3
4
5
6
7
8
9
10

# PyQt5的一些报错
1. 点击run里面的edit configurations
2. 将Emulate terminal in output console勾上后点击apply应用
## ComboBox报错
> argument 1 has unexpected type 'NoneType'

[解决方法](https://deepinout.com/pyqt5/pyqt5-questions/47_pyqt5_argument_1_has_unexpected_type_nonetype.html):`connect`中传的函数加一个`lambda`
```ruby
self.ui.comboBox_ImgType.currentIndexChanged.connect(lambda : self.RadiomicsFunc.ImgType_init(self.comboBox_ImgType.currentIndex()))

graphicsView刷新

QLayout: Attempting to add QLayout “” to QGraphicsView “graphicsView_4”, which already has a layout

DataProgress

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from radiomics import featureextractor
import pandas as pd
import numpy as np

class RadioMLProgress(object):

def extract(self, images, name, nii_path, ori_path):
self.ori_path = ori_path
#self.ori_path_2 = ori_path_2
#self.ori_path_3 = ori_path_3
#self.ori_path_4 = ori_path_4
# 创建一个特征提取器对象, ori_path_2, ori_path_3, ori_path_4
'''
#特征提取器与分组
settings = {}
settings['sigma'] = [3, 5]
extractor.enableImageTypeByName('LoG')


settings['binWidth'] = 25 # 5

#settings['Interpolator'] = sitk.sitkBSpline
settings['resampledPixelSpacing'] = [1, 1, 1] # 3,3,3
settings['voxelArrayShift'] = 1000 # 300
settings['normalize'] = True
settings['normalizeScale'] = 100


extractor.enableImageTypeByName('Wavelet')

extractor.enableImageTypeByName('Wavelet')
extractor.enableImageTypeByName('Wavelet')
extractor.enableImageTypeByName('Square')
'''
# 定义要计算的特征
#params = {}
features = [
'exponential_glrlm_GrayLevelNonUniformity',
'lbp-3D-m1_glszm_LargeAreaLowGrayLevelEmphasis',
'logarithm_gldm_GrayLevelNonUniformity',
'logarithm_glrlm_GrayLevelNonUniformity',
'logarithm_glszm_GrayLevelNonUniformity',
'log-sigma-3-0-mm-3D_glrlm_LowGrayLevelRunEmphasis',
'log-sigma-3-0-mm-3D_glszm_ZoneEntropy',
'log-sigma-4-0-mm-3D_firstorder_RootMeanSquared',
'log-sigma-4-0-mm-3D_glrlm_GrayLevelNonUniformity',
'log-sigma-4-0-mm-3D_glszm_ZoneEntropy',
'log-sigma-5-0-mm-3D_glrlm_GrayLevelNonUniformity',
'log-sigma-5-0-mm-3D_glszm_ZoneEntropy',
'original_firstorder_Kurtosis',
'original_glrlm_GrayLevelNonUniformity',
'original_glszm_GrayLevelNonUniformity',
'original_shape_Maximum2DDiameterColumn',
'original_shape_Maximum2DDiameterRow',
'original_shape_Maximum2DDiameterSlice',
'original_shape_Maximum3DDiameter',
'original_shape_Sphericity',
'original_shape_SurfaceArea',
'square_glcm_Idmn',
'square_glszm_GrayLevelNonUniformity',
'squareroot_gldm_GrayLevelNonUniformity',
'squareroot_glrlm_GrayLevelNonUniformity',
'squareroot_glszm_GrayLevelNonUniformity',
'wavelet-HHL_glszm_LargeAreaEmphasis',
'wavelet-HHL_glszm_ZoneVariance',
'wavelet-HLH_glszm_LargeAreaEmphasis',
'wavelet-HLH_glszm_LargeAreaLowGrayLevelEmphasis',
'wavelet-HLH_glszm_ZoneVariance',
'wavelet-LHH_glszm_LargeAreaEmphasis',
'wavelet-LHH_glszm_LargeAreaLowGrayLevelEmphasis',
'wavelet-LLL_firstorder_Kurtosis',
'wavelet-LLL_glcm_Imc2',
'wavelet-LLL_glrlm_GrayLevelNonUniformity',
'wavelet-LLL_glszm_GrayLevelNonUniformity'
]
# 创建特征提取器,并设置要提取的特征列表
params = {'enabledFeatures': features}
extractor = featureextractor.RadiomicsFeatureExtractor(**params)
#params['normalize'] = True # 对图像进行标准化
#params['enabledImageTypes'] = {'Original': {}, 'Wavelet': {}} # 只使用原始图像和小波图像特征
#params['enabledFeatures'] = ['glrlm', 'glszm'] # 指定只提取 glrlm 和 glszm 特征
#extractor = featureextractor.RadiomicsFeatureExtractor(**params)
#extractor.enableFeatureClassByName()

# 加载图像数据
# 注意:需要替换为你的实际图像路径
# 使用 extract 方法来计算特征
result = {}
for modality, file_path in images.items():
if nii_path:
result[modality] = extractor.execute(file_path, nii_path)
else:
result[modality] = extractor.execute(file_path)
# 输出特征结果
for modality, features in result.items():
print(f"Features for {modality}:")
for feature_name, value in features.items():
print(f"\t{feature_name}: {value}")
# 储存数据
for modality, features in result.items():
save_path = name + '_for_' + modality + '.csv'
save_path_txt = name + '_for_' + modality + '.txt'
df = pd.DataFrame()
for key, value in features.items():
# 如果当前特征是所选特征之一,则将其添加到 DataFrame
# if key in selected_features:
df = df.append({'Feature': key, 'Value': value}, ignore_index=True)
# 将 DataFrame 保存为 CSV 文件
df.to_csv(save_path, index=False)
df.to_csv(save_path_txt, index=False)

self.saveFile(result)

def saveFile(self, result):
def is_number(s):
try:
float(s)
return True
except (TypeError, ValueError):
pass
try:
import unicodedata
unicodedata.numeric(s)
return True
except (TypeError, ValueError):
pass
return False
#df = pd.read_csv('./Features_Result_for_T1.csv')
numdata = pd.DataFrame() # 数字特征
# 储存数据
for modality, features in result.items():
save_path = './lib/Estimate/Prediction_for_' + modality + '.csv'
df = pd.DataFrame()
for key, value in features.items():
# 如果当前特征是所选特征之一,则将其添加到 DataFrame
# if key in selected_features:
df = df.append({'Feature': key, 'Value': value}, ignore_index=True)

for idx, row in df.iterrows(): # 遍历 DataFrame
if is_number(row['Value']): # 存储数字结果
numdata = numdata.append({'Feature': row['Feature'], 'Value': row['Value']}, ignore_index=True)
numdata = numdata.drop(df.index[0])
numdata.to_csv(save_path, sep=',', index=False)

引子

一个简单的小例子

  1. 通过函数,传入可变参数,最后输出值return是字典,不需要传入的参数是预设的。
  2. 自动给相同类型属性的变量赋值
  3. 实体,属性,功能….
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    attack_vals = {
    "A":30,
    "B":50,
    }
    def dog(name,d_type):#传参的模板
    data = {
    "name":name,
    "d_type":d_type,
    "attack_val":30,
    "life_val":100
    }
    #自动给相同类型属性的变量赋值

    def dog_bite(person_obj) #编辑对象的特定动作,绑定不出错儿~
    person_obj["life_val"] -= data["attack_val"] #执行动作
    #由于data就是在这个函数里定义的,所以直接调用data就可以
    #将函数定义在“狗”的内部,避免被错误地调用
    data["bite"] = dog_bite
    #确保外部可以顺利调用

    if d_type in attack_vals:
    data[attack_val] = attack_vals[d_type]
    else:
    data[attack_val] = 15 #给没有名字的一个默认值
    return data #输出字典类型

    #以此类推可以生成多个实体
    d1 = dog("lym","A")
    d1["bite"](person) #调用dog_bite函数

面向过程 & 面向对象

面向过程编程(Procedural Programming)

  • top-down language,程序从上到下一步步执行,解决问题(分解问题思想)
  • 简单的小脚本,可扩展性差

面向对象编程(Object-oriented Programming)

class : 类

  1. 首字母大写+驼峰命名法;
  2. 属性:类中的变量;
    属性只能通过实例来调用实例.属性
  • 类下直接定义的属性:公共属性,每一个实例会共享这个值。
    公共属性可以直接在外部修改,对单独的实例进行修改就可以~
  • 初始化方法\构造方法\构造函数
    实例化时会自动执行,进行一些初始化工作。
  1. 用于构造一些私有化属性(传到参数里);
    实例属性只被实例享有,不会存到类里,调用报错。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A:
    def __init__(self,attribute1,attribute2):
    #如果真正把属性传到实例,要把两个值和实例绑定:
    self.a1 = attribute1
    self.a2 = attribute2
    #在本函数中可以直接调用,不加实例;
    #外部调用attribute1,2时调用方式与公共相同相同;

    #实例化时传入参数:
    exm = A(attA,attB)
  2. 方法: 类中的函数、功能;
    self: 代表实例本身,必须是方法中第一个参数;
  3. 实例化:将类变为实体

基本语法

关键字global的用法

global是Python中全局变量的关键字,可以使得一个局部变量变为全局变量(既可以是某对象函数创建,也可以是在本程序任何地方创建,可以被本程序的所有对象或函数引用)
注意如果使的函数可以改变全局变量的值,该全局变量声明必须在函数内
示例:

1
2
3
4
5
6
7
8
9
10
11
12
x = 4

def my():
global x
x = 8
print("x = ",x)

print("x = ",x)
my()
print("x = ",x)

#输出顺序:4 8 8

字典添加元素

  1. 单个添加,并且是在新元素的基础上添加(对已有元素会覆盖
    book_dict["owner"] = "tyson"

异常

Python被称为异常的对象用于管理程序执行期间发生的错误,当程序发生错误时可以使用try-except语句来处理错误,保持程序运行。

  • 程序崩溃时让用户看到traceback并不是好事,不仅是不美观,并且可能会泄露你的程序的信息,比如你程序的文件名称和无法正确运行的部分代码,攻击者很有可能根据这些信息判断出可以对代码发起怎样的攻击。

处理traceback的错误

哪条语句导致错误就把哪条语句放在try后边

1
2
3
4
try:
"""可能导致程序错误运行的代码块"""
except [错误名(可以在traceback中看到)]:
"""处理错误的代码块"""

如果后面还有其他代码块,程序将继续运行。如果有一些代码仅在try在执行时运行,可以使用else语句

1
2
3
4
5
6
try:
"""可能导致程序错误运行的代码块"""
except [错误名(可以在traceback中看到)]:
"""处理错误的代码块,或者说使用pass不做任何反应"""
else:
"""在try-except语句中没有错误发生时运行的代码块"""

程序种的实用方法

判断

是否为数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def is_number(s):
try:
float(s)
return True
except ValueError:
pass

try:
import unicodedata
unicodedata.numeric(s)
return True
except (TypeError, ValueError):
pass

return False

Python对文件的操作

基本语法

  1. file = open('your_file.txt','r')操作方式参数’w’会改成写(创建)文件原有内容会被清空;’r’是只读;’a’是直接在尾部添加内容或者创建;
  2. f.close()关闭文件
  3. f.read()文件读取
  4. 常用的文件操作形式Img

with上下文打开(推荐)

操作完成后无需通过close()关闭文件,并且安全系数更高

1
2
3
import codecs
with codecs.open('your_file.txt','r','utf-8') as f:
f.write('WellDone!')

yaml文件进行操作

ymal文件操作的一个简单小教程

yaml语法

  • 支持的数据类型:
    字典、列表、字符串、布尔值、整数、浮点数、Null、时间等
  • 基本语法规则:
  1. 大小写敏感
  2. 使用缩进表示层级关系
  3. 相同层级的元素左侧对齐
  4. 键值对用冒号 “:” 结构表示,冒号与值之间需用空格分隔
  5. 数组前加有 “-” 符号,符号与值之间需用空格分隔
  6. None值可用null 和 ~ 表示
  7. 多组数据之间使用3横杠—分割
  8. #表示注释,但不能在一段代码的行末尾加注释,否则会报错

第三方库PyYAML

读取/写入后各类型对应的格式

写入数据

  • 打开后首先加入 yaml.dump(data=mydata, stream=f,allow_unicode=True)防止写入的中文乱码
  • 多组数据写入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import yaml

    apiData1 = {
    "page": 1,
    "msg": "地址",
    "data": [{
    "id": 1,
    "name": "学校"
    }, {
    "id": 2,
    "name": "公寓"
    }, {
    "id": 3,
    "name": "流动人口社区"
    }],
    }

    apiData2 = {
    "page": 2,
    "msg": "地址",
    "data": [{
    "id": 1,
    "name": "酒店"
    }, {
    "id": 2,
    "name": "医院"
    }, {
    "id": 3,
    "name": "养老院"
    }],
    }

    with open('./writeYamlData.yml', 'w', encoding='utf-8') as f:#注意这里文件名一定要用单引号,不然会出现奇怪的错误ing
    yaml.dump_all(documents=[apiData1, apiData2], stream=f, allow_unicode=True)#若希望写入单组数据,使用dump,document改data

Pandas

csv

读取

注意你如果要把其他文件的格式转换成csv再读取,一定要使用表格工具另存为csv,而不是直接修改他的后缀名,直接修改后缀会导致无法打开的错误发生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd

csv_path='./result.csv'

# 使用pd.read_csv读取数据
contents=pd.read_csv(csv_path)

# 查看前几行数据
print(contents.head())

# 查看数据的形状,返回(行数、列数)
print(contents.shape)

# 查看列表列的名称
print(contents.columns)

# 查看索引行
print(contents.index)

# 查看每列的数据类型
print(contents.dtypes)

  1. 获取信息
    1
    2
    3
    4
    # 列名
    print(df.columns)
    # 索引
    print(df.index)
  2. 统计数据个数
    1
    2
    3
    4
    5
    import pandas as pd

    df = pd.read_csv("filename.csv")
    count = df.shape[0]
    print("数据个数:", count)

输出

  1. 打印函数
    1
    2
    3
    4
    5
    6
    print(type(df))  # pandas.core.frame.DataFrame 类
    print(df.shape) # DataFrame 大小
    print(df.head(5)) # 打印头部 5 行
    print(df.tail(5)) # 打印尾部 5 行
    print("1")
    print(type(df['Feature'])) # pandas.core.series.Series 类
    1
    2
    datacsv = pd.read_table('python.csv', ',')
    #将文件内的所有数据全部按照逗号进行分隔。

Python调用

cmd

和gpt老师的对话解决了我的问题

调用cmd来执行R语言脚本

最大的问题在于,实际上在python中敲入exit后并不会结束cmd的进程,这时候会陷入一直循环,在python中也无法读出是否有输入输出等等,所以此处通过输出间隔时间来判断程序是否结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import subprocess
import time
import time

import select

# 启动一个交互式命令行
process = subprocess.Popen(["cmd"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
#process = subprocess.Popen(cmd_commands, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

FeaNum = '20' + '\n'
# 定义要执行的多个命令
commands = [
"cd Estimate\n",
"Rscript .\RadioML_features.R -p TIANTAN -t 5 -n 1000 -R prefix_1000_5_training.RData\n",
FeaNum
]

# 发送多个命令到命令行
for command in commands:
process.stdin.write(command)
process.stdin.flush() # 刷新缓冲区以确保命令被发送
# 读取命令的输出
if process.stdout.readline():
output_line = process.stdout.readline()
if not output_line: # 如果没有更多输出,则退出循环
#process.stdin.write("exit\n")
print("Finished")
break
print(output_line.strip())
time.sleep(1) # 等待一秒钟以确保命令有足够的时间执行
if command == FeaNum: # 如果没有更多输出,则退出循环
print("Finished")

output_line = process.stdout.readline()
start_time = time.time()
while output_line:

print(output_line.strip())

# 设置超时时间为1秒
#print(time.time() - start_time)
if time.time() - start_time >= 0.85:
break

output_line = process.stdout.readline()

下面是chat给的跳出循环方案,测试证明’Finished’根本不会输出

1
2
3
4
5
6
7
8
9
10
# 读取命令的输出
while True:
output_line = process.stdout.readline()
if not output_line: # 如果没有更多输出,则退出循环
process.stdin.write("exit\n")
print("Finished")
break
print(output_line.strip())
sys.exit(process)
#这个地方不要加wait,否则会一直退不出来

Pycharm救命小操作

  1. 查看文件历史

AndrewNg

看着吴恩达老师的网课稍微用自己的理解来记一记东西~非常业余只供看乐子

Concepts

  • Structured Data : Refers to the data that each of the features has a very well defined meaning.
  • Unstructured Data : audio,raw audio or images where you may want to recognize what’s in the image or text. And the features might be the pixel values(像素值) in an image.What should be paid attention to is that humans are really good at interpret unstructing data,as a word or a text can be regarded as a form of unstructured data.
  • piont: deal with a huge amount of data.Especially “labled data”
  • algorithm innovation(data computation): transform sigmoid to RELU function(help computation),more convient for bigger nn trains and trying your new idea,improving your efficiency.
  • Label : things you’re going to predict.
  • Feature :
  • Example : labened/unlabeled(features only) ;
  • model :
  • training :
  • inference :
  • overfitting ;
  • convergence :
  • parameter :
  • hyperparameter :
  • 模型训练的一次迭代(一次梯度更新)

Basics

Logistic Regression

  • The dimension of feature vector:the number of elements.n=num*row*col(of the matrixes);m refers to the number of examples we have in total;
  • create a matrix (nx,m);python:X.shape;Y.shape(1,m)
  • We assume that the parameters(参数) of logistic regression will be w an nx-dimensional vector.
  • you can use the following function to get a estimated value:
    $$
    \widehat{y}=\sigma(wx+b)
    $$
  • Loss Function: Logistic Rsgression lost function,we can find the local optima(best solution).You can assume y equals to a certain value like 1 or 0,then see what value we hope the $\widehat{y}$ be.
    $$
    L(\widehat{y},y)=-(ylog\widehat{y}+(1-y)log(1-\widehat{y}))
    $$
  • Cost Function: It is the additive sum of every loss of the predictive value,and it measures how well the parameters w and b are doing. We should find apprioate w and b to make the J(w,b) as small as possible.
    $$
    J(w,b) = \frac{1}{m}\sum L(\widehat{y}^{(i)},y^{(i)})
    $$
    Interpret:
    First, we want to minimum the cost function J(w,b).
    Second, we’re supposed to maximum the minus L, for we always want the max through the maximum likehood estimition.
    Then we assume that our model were IID(identically independently distributed).
    $$y = 1 : p(y|x) = \widehat{y}$$
    $$y = 0 : p(y|x) = 1 - \widehat{y}$$
    We mix the two function together:
    $$p(y|x) = \widehat{y}^y + (1-\widehat{y})^{(1-y)}$$
    log the both side we get the -L.

Use the gradient descent algorithm

  • to train the w and b,get the best result;
  • progress:as you should know that J(w,b) is a convex(凹函数),so we’re supposed to find the minus,at least the local minus. so we repeat:
    $$
    w=w-\alpha \frac{\delta J(w,b)}{\delta w}
    $$

Implement gradient for logistic regression

  • three core formulas:
    $$z = w^Tx+b$$
    $$\widehat{y} = a = \sigma(z)$$
    $$L(a,y) = -(ylog(a)+(1-y)log(1-a))$$
  • the number of fearures equals to the unmber of w(i),and only a sigle b.We compute the loss based on a sigle example.
  • Caculate:
    $$
    dz = a - y , db = dz
    $$
    then you cancompute $w_1,w_2$….the same way as the former
    $$dw_1 = x_1dz,w_1 = w_1 - \alpha dw
    $$

for the m examples,make a for loop

  • you add every value up,include $J,w_i,z_i,dz_i$,then you get the additive sum of $j,dw_i,b$,then /m.
    $$w_i = w_i - \alpha dw_i
    $$
    $$b = b - \alpha db$$

Vectorization

  • use special command so that you can accelarate your efficiency.for loop is too slow.
  • feature: transform the for loop compute to the special matrix algorithm,so we can call out numpy to perform better.Second you can achieve work out all the results at a sigle time through transform the vector to the matrix.

broadcasting in Python

1
2
cal = A.sum(axis = 0) # to sum vertically, 1 means sum horizontally
percentage = 100*A/(cal.reshape(1,4)) # make sure that the shape is you want

In fact, Python can automatically transforms the matrix too suit the compute, by copying vertically or horizontally.
You can read NumPy documention.

Do not use rank 1 arries

1
2
a = np.random.dandn(5) # to create a special arry ranking 1
a.shape = (5,) # rank = 1, behavior differently from vector

instead do this

1
2
3
4
a = np.random,randn(5,1) # column vector
a = np.random.randn(1,5) # row vector
assert(a.shape == (5,1)) # to confirm the shape
a = a.reshape((1,5)) # to ensure its behavior

Neural Network

Overview&Representation

  • input layer -> hinden layer -> output layer
  • we call the input layer the zero layer, according to that role to define the number of layer.

西瓜书

性能度量

错误率精度略,这里主要说明查准率与查全率
查准率:
$$
P = \frac{TP}{TP+FP}
$$
查全率:
$$
R = \frac{TP}{TP+FN}
$$
直观反映:PR图

神经网络

前馈型(FNN)

  • 最简单
  • 各神经元之间没有反馈连接,信息只能向前流动
  • 没有记忆效应,适用于多监督学习任务

典型:卷积神经网络(CNN)

后馈型(递归型)

  • 具备反馈/循环连接
  • 输出可以在后续的时间步骤被送回给网络的输入端
  • 适于处理需要记忆和上下文信息的任务,处理时序数据

典型:循环神经网络(RNN)

LSTM

一种改进后的RNN

非线性分类器

升高维度

决策树

随机森林

GBDT

SVM(非线性核)

多层感知机