草庐IT

手把手 | 教你打包Python脚本并用TBtools完成程序界面化

生信石头 2023-10-26 原文

导语

前述,师弟 Chuhao Li 用 Python 开发了第一个 TBtools CLI Program Wrapper Creator 插件。插件的实用性和制作的精细程度出乎意料。感慨之余,我邀请他给大伙分享一下制作经验(因为我完全不懂 Python,而我知道 Python 是现在绝大多数朋友最喜欢用的语言)。收到推文,发现非常详细,且内容使用。尤为难得,还直接拓展了 TBtools 的一个功能痛点(没有批量化反向互补功能!)。Chuhao 开发的这个插件,直接填补了这个缺憾,同时还做了多线程加速,我用了下,非常方便,非常快!现在插件已经上传到商店,感兴趣的朋友可下载使用。另,欢迎大伙学习这篇博文后,也一起来开发插件,贡献力量,一起减除更多人的生信数据分析负担。


前言

相信很多学习生物信息学的人都会学习python。你可能遇到过这样的情况:

在一个风和日丽的早上,你耗尽洪荒之力,写好了一个“反向互补”命令行工具,然后很激动地把代码发到群里面,分享给实验室其他人用。

小伙伴就会跟你说:这个脚本要怎么用呀?然后,你就会解释道:首先,安装一个python,然后这样打开终端,然后这样输入参数……。

小伙伴说:“嗯,我先试一下”。然后一顿操作,在不知名网站上下载了个“python”安装包,把python安装好,还付送了一堆4XX9小游戏之类的软件。

如无意外,过一会,小伙伴就会跑过来问你,“我输入命令,怎么报错了”?

你仔细地检查,发现如下问题:

  • 安装了python2而不是python3
  • 那行命令没有在特定工作目录
  • 命令的某个单词少了一个字母
  • 某两个参数之间没有空格

一顿操作过后,问题解决了,你还很详细地给小伙伴解释了其中的原理。能帮助到实验室的小伙伴,你很开心,回到工位上,准备迎接一天的工作。

然后,噩梦来了。另外一个小伙伴又来问你同样的问题……

这时候,我会告诉你:用pyinstaller打包你的工具,然后用TBtools的CLI Program Wrapper Creator来快速构建一个图形界面插件。

5分钟把插件打包好,直接把插件扔给小伙伴们,在群里说:“我项目需求,顺手写了个工具,有需要的拿去。” 然后潇洒离去。

下面我将用实际例子给大家介绍,如何操作。

python反向互补脚本

下面的脚本实现了单条序列的反向互补。为了减少代码量,我使用了biopython来进行fasta序列的读写和操作。接下来,我要把这个工具打包成TBtools插件。

import sys
from Bio import SeqIO

infile = sys.argv[1]
outfile = sys.argv[2]

seq = SeqIO.read(infile, 'fasta')
seq.seq = seq.seq.reverse_complement()

with open(outfile, "w") as f:
    SeqIO.write(seq, f, "fasta")

安装pyinstaller

假设你已经安装好了python,进入cmd或者powershell,输入以下命令,即可安装pyinstaller以及脚本所依赖的biopython

注意:如果你的python环境中已经安装了很多别的包,那么pyinstaller打包的时候会把一些不必要的包打包进来。这种情况下,建议使用virtualenv来新建一个新的环境(如果你是刚刚才安装的python,那就没必要做这一步了):

py.exe -m pip install virtualenv

py.exe -m virtualenv my_env

.\my_env\Scripts\activate # 激活环境。激活后可以看到命令行左边多了(my_env)。

# 如果激活失败,那就尝试不要用powershell,转用cmd(新建一个cmd窗口或者在powershell中直接输入cmd即可)。

环境激活之后,安装所需的软件。这里除了pyinstaller,还安装了biopython,这是这个脚本中用到的。

py.exe -m pip install pyinstaller biopython

一般来说,安装完后,在cmd或者powershell下运行下面的命令可以打印出帮助信息。

pyinstaller -h

如果不行,那就试试下面的(注意大小写):

py.exe -m PyInstaller -h

编写脚本需要注意的问题

参数传递

可以使用位置参数(参数前不带“-”的)和长参数(参数前带“--”的)。如果只有位置参数,就直接用sys.argv;如果要用到长参数,推荐使用argparse

值得注意的是,当argparsemultiprocessing一起使用的时候,需要使用parser.parse_know_args(),而不能直接使用parser.parse_args(),否则传递参数的时候会出错。

多线程

使用到多线程时,主程序需要用if __name__ == '__main__':保护起来。下面一行紧接着freeze_support()

调用其他工具

假如我的程序需要调用其他二进制文件,只需要把exe文件放在与脚本同一个目录下即可。TBtools运行脚本的时候,会自动把脚本所在目录添加到环境变量,于是在脚本中可以不需要指定二进制文件所在的路径。

调用外部命令,注意给输入、输入文件路径加引号,防止输入文件路径中有空格的问题。假设输入文件路径为infile = "C:\Use r\input.fasta",中间有个空格。那么,如果使用os.system(f'tool_name.exe {infile}')来调用就会有问题。需要在infile两边加上引号:os.system(f'tool_name.exe "{infile}"')

值得注意的是,如果使用的是blastn.exe等TBtools已经有的二进制文件,则可以直接调用,不必打包到插件当中。

代码修改

下面的代码,在原来的基础上添加了多线程和使用argparse来解析参数。请注意注释部分。

import argparse
from multiprocessing import Pool, freeze_support
from Bio import SeqIO

def rc(seq):
    seq.seq = seq.seq.reverse_complement()
    return seq

if __name__ == '__main__': # 使用到多线程时,这一行必须加。下面一行紧接着“freeze_support()”
    freeze_support() 
    
    parser = argparse.ArgumentParser()
    parser.description = "多线程反向互补"
    parser.add_argument("--threads", default=1, type=int, help="Number of threads")
    parser.add_argument("input", help="Input fasta file")
    parser.add_argument("output", help="Output fasta file")
    
    args, unparsed = parser.parse_known_args() 
    # argparse和多线程搭配使用的时候,这里需要使用parse_know_args(),
    # 而不能直接使用parse_args(),否则传递参数的时候会出错。

    seqs = SeqIO.parse(args.input, 'fasta')
    with Pool(processes=args.threads) as pool:
        seqs_rc = pool.map(rc, seqs)
        pool.close()
        pool.join()

    with open(args.output, "w") as f:
        for seq in seqs_rc:
            SeqIO.write(seq, f, "fasta")

打包

具体操作

在py脚本所在目录,按住“shift”键,然后右键点击文件浏览器的空白处,点击“在此处打开命令窗口”,就能进入终端,并自动切换工作目录为当前目录。

运行以下命令,即可打包:

pyinstaller -D .\multiprocess_rc.py

# 如果不行,就用下面的:
# py.exe -m PyInstaller -D .\multiprocess_rc.py

打包完成,可以看到当前目录多了两个文件夹和一个文件:

其中dist文件夹里面就是我们打包好的工具。里面长这样。里面的multiprocess_rc.exe就是程序的入口,其他文件是该程序依赖的资源,必须和主程序放在同一个目录。:

命令解析

在这个行命令中,我传递了-D.\multiprocess_rc.py两个参数给pyinstaller

  1. -D 参数是指要打包成一个文件夹,把所有依赖的dll等文件放在同一个文件夹中。另外一种方式是-F,指定-F参数可以把全部文件打包成单个的exe文件。虽然-F参数打包出来的文件看上去很干净,其实在实际运行的时候还是会把exe文件里面的东西释放到一个临时文件夹中,然后再运行,而且会引入一些额外的bug。所以,我推荐使用-D参数。

  2. .\multiprocess_rc.py 就是脚本的主程序。如果有多个模块,只需要输入主程序即可。打包工具会自动识别主程序中有import到的其他脚本,一起打包进去。

其他实用参数

如果你需要调用其他二进制文件,比如说tool.exe。你可以把tool.exe放在和脚本同一目录,然后打包的时候增加以下参数:--add-data "tool.exe;tool.exe"。这样就可以把tool.exe一起放在打包好的文件夹中了。当然你也可以手动复制过去,不过如果涉及到多次打包,就会麻烦一丢丢。

输出文件

  • dist 目录:里面有打包好的软件。

  • build 目录:打包过程中生成的临时文件,可以删掉。

  • *.spec 文件:打包相关的配置文件。如果重复打包,可以直接把这个文件代替掉脚本名字,传递给pyinstaller,就不用再写那些参数了。

小技巧

常常会遇到需要打包、测试多次的时候。这时候,把打包和测试相关的命令写入到一个.bat文件中是比较方便的。这样直接双击.bat文件就可以重新打包,无需重新输入命令。

关于pyinstaller的更多信息,请参考:

测试pyinstaller打包结果

进入dist/multiprocess_rc/目录,打开cmd,执行以下命令:

.\multiprocess_rc.exe --threads 4 .\input.fasta output.fasta

可以得到输出结果,代表运行成功。检查文件结果,也确实是反向互补了:

input.fasta:

>test
ATCGGGTTCCAA
>test1
ATCGGGTTCCAA
>test2
ATCGGGTTCCAA
>test3
ATCGGGTTCCAA
>test4
ATCGGGTTCCAA

output.fasta:

>test
TTGGAACCCGAT
>test1
TTGGAACCCGAT
>test2
TTGGAACCCGAT
>test3
TTGGAACCCGAT
>test4
TTGGAACCCGAT

打包TBtools插件

在插件商店安装好CLI Program Wrapper Creator后,从Tbtools的菜单栏Others > Plugin中即可看到。点击进去,参照下方的设置:

配置完成,点击“Refresh”就可以刷新界面。还可以直接在界面中运行程序,非常方便。如果配置到一半要去吃饭,可以按下方的“Save Config”暂时把界面设置保存为文件,下次可以直接用“Load Config”来加载,省去很多时间。

测试完成,点击右下角的“Export Plugin”,即可导出插件。导出的文件实际上是一个zip压缩文件。建议导出的时候不要加任何后缀,这样后面安装插件时,菜单栏上名字就会带有后缀。导出之后,再重新添加“.zip”后缀。

导出后,进入Others > Install Plugin 安装插件,然后就可以使用了。

把序列粘到Input输入框,点击开始,就可以看到下方的反向互补序列了!

这里我忽略了很多UI设计界面使用的细节,详情可以回头看看公众号前几天的推文,有CJ的详细教程。

总结

把经常要做的、重复性的东西简单化、流程化、自动化,或许可以让大家把更多的时间投入到科学问题的研究中。

有关手把手 | 教你打包Python脚本并用TBtools完成程序界面化的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  4. ruby - 匹配大写字母并用后续字母填充,直到一定的字符串长度 - 2

    我有一个驼峰式字符串,例如:JustAString。我想按照以下规则形成长度为4的字符串:抓取所有大写字母;如果超过4个大写字母,只保留前4个;如果少于4个大写字母,则将最后大写字母后的字母大写并添加字母,直到长度变为4。以下是可能发生的3种情况:ThisIsMyString将产生TIMS(大写字母);ThisIsOneVeryLongString将产生TIOV(前4个大写字母);MyString将生成MSTR(大写字母+tr大写)。我设法用这个片段解决了前两种情况:str.scan(/[A-Z]/).first(4).join但是,我不太确定如何最好地修改上面的代码片段以处理最后一种

  5. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  6. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  9. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  10. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

随机推荐