python | 转调器——从构想到实现

2022-10-22 11:05:30

终于把转调器做出来了,上个学期还觉得这是个遥不可及的事物,因为涉及到一些括号、字符空格的处理,便觉得非常头疼,但这次一点点把逻辑整出来,渐渐得可以实现了。

一、转调的逻辑实现

(一)困难罗列:

难题当然有很多啦,这里按照解决难度分成三类:
难度C:

难度等级问题
C1、转调括号的中英文差异;
2、处理无效的#;
B1、如何将#1视为一个整体进行转调
2、空格的有效、无效判断问题;
3、用什么方式对音符进行替换
A1、括号多余、缺失问题;
2、转调后括号处理问题;

还有很多细节问题,就不一一罗列了(也忘得差不多了) 。

(二)解决逻辑

下面用字母+数字表示具体问题,如C1表示转调括号的中英文差异

对于C1,用字符串的replace函数,统一将中文括号【】()转成英文括号就ok了。

对于C2,如果#后面的下一个字符是数字1-7(如果下一个字符是空格,则继续推后,以此类推),则#有效,保留在字符串中,如果后面不是数字1-7,则删掉。

对于B1,由于输入的是字符串,先用程序将#1切成一片,然后将整个字符串转成列表,列表中的每一个元素有这些类型:括号的一半、空格、#数字、数字、无意义字符,方便接下来处理

对于B2,对空格的处理要分为转调前的处理和转调后处理,具体解决看代码(其实是忘了)

对于B3,这里有几个方案:
方案一:括号里面的和括号外面的分别写两个函数进行处理,如12(23)2,12一个函数处理,然后检测到括号,调用函数处理,最后处理2的转调,但感觉很困难,在识别括号范围就是个难题,如果出现括号之中又出现括号,又调用一次函数,这样会造成递归,想想还是算了(可以实现,但我不能)
方案二:用索引的方法,给出字符串,在小括号里面的索引是-1,大括号里是1,遇到‘(’和‘】’就-1,遇到‘)’和‘【’就+1,没遇到括号就保持0,如12(12)【23】的索引值是00-1-1-101110,然后根据索引值给原字符串(这时转成列表了)进行括号的转变,变成12(1)(2)【2】【3】(括号已经被删掉),可以转调了,这里又有个问题,就是有时会出现((2))这样的列表元素,所以我们无视括号,将2进行转调,然后将转调结果补上两小括号。之后的事情又是对括号的处理了。

对于A1,处理规则每个人都不同,我这里重视左括号,轻视右括号,即有多余的左括号补上右括号,有多余的右括号就删掉右括号,实际处理更加复杂,详情看代码。

对于A2,就是接着B3来的,B3会出现这样的结果(2)(3),为了美观,我们要将其转成(23),为了偷懒,这部完全可以不做,但有时会出现(【2】)这样的东西,混淆视听,这里的处理方法还是索引的方法。

总的来说,解决思路就是用find和re方法找到位置,增删补改,然后在字符串和列表中反复横跳,来处理转调前后括号的遗留问题,其实都是非常基础的。

三、代码实现

本来想用下class方法的,可惜……

#change.pyimport re

syllable=['(1)','(#1)','(2)','(#2)','(3)','(4)','(#4)','(5)',\'(#5)','(6)','(#6)','(7)','1','#1','2','#2','3','4',\'#4','5','#5','6','#6','7','[1]','[#1]','[2]','[#2]'\,'[3]','[4]','[#4]','[5]','[#5]','[6]','[#6]','[7]']######################################预处理,将输入转换成可识别的状态####################################defPretreatment(talk):#将中文括号转换成英文
    talk= talk.replace('(','(')
    talk= talk.replace('【','[')
    talk= talk.replace(')',')')
    talk= talk.replace('】',']')#将错误的括号转成正确的
    index1=0
    index2=0while((talk.find('(',index1)!=-1)or(talk.find(')',index2)!=-1)):
        index1_temp= talk.find('(',index1)
        index2_temp= talk.find(')',index2)#print(index1_temp,index2_temp)if index1_temp<index2_temp:#左括号位置比右括号前if index1_temp==-1:#如果左括号不存在
                talk= talk[0:index2_temp]+ talk[(index2_temp+1):len(talk)]else:#一对标准的括号
                index1= index1_temp+1
                index2= index2_temp+1elif index1_temp>index2_temp:if index2_temp==-1:#右括号不存在
                talk= talk+')'
                index2=len(talk)-1
                index1= index1+1else:#右括号在左括号前面
                talk= talk[0:index2_temp]+ talk[(index2_temp+1):len(talk)]
                index1= index1_temp-1while(talk.find('()',0)!=-1):
        talk= talk.replace('()','')#处理中括号
    index1=0
    index2=0while((talk.find('[',index1)!=-1)or(talk.find(']',index2)!=-1)):
        index1_temp= talk.find('[',index1)
        index2_temp= talk.find(']',index2)#print(index1_temp,index2_temp)if index1_temp<index2_temp:#左括号位置比右括号前if index1_temp==-1:#如果左括号不存在
                talk= talk[0:index2_temp]+ talk[(index2_temp+1):len(talk)]else:#一对标准的括号
                index1= index1_temp+1
                index2= index2_temp+1elif index1_temp>index2_temp:if index2_temp==-1:#右括号不存在
                talk= talk+']'
                index2=len(talk)-1
                index1= index1+1else:#右括号在左括号前面
                talk= talk[0:index2_temp]+ talk[(index2_temp+1):len(talk)]
                index1= index1_temp-1while(talk.find('[]',0)!=-1):
        talk= talk.replace('[]','')#print(talk)#return talk#将括号放到正确的位置,如'sdfs(  ed(     '变成'sdfs  (ed     ('while(re.findall(r"\(\s+|\[\s+|\s+\)|\s+\]",talk)!=[]):
        b= re.findall(r"\(\s+|\[\s+|\s+\)|\s+\]",talk)#print(b)
        index=0for iin b:
            m=len(i)#print('长度:',m)
            d= talk.find(i,index)
            talk= talk[0:d]+ i[::-1]+ talk[d+m:len(talk)]#print('位置:',d)#对#号的处理:   'while(re.findall(r"\#\s+",talk)!=[]):
        index=0
        b= re.findall(r"\#\s+",talk)#print(b)for iin b:
            m=len(i)#print('长度:',m)
            d= talk.find(i,index)
            talk= talk[0:d]+ i[::-1]+ talk[d+m:len(talk)]
            index= d+ m#print('位置:',d)#去除掉无效的#
    index=0while(talk.find('#',index)!=-1):
        b= talk.find('#',index)#print(b)if talk[b+1]notin['1','2','3','4','5','6','7']:
            talk= talk[0:b]+ talk[b+1:len(talk)]
            index= belse:
            index= b+1#print(talk)return talk##########################################将#和数字结合起来,并转成列表形式#########################################defyuchuli(talk):   
    talk=list(talk)#print(talk)#print(len(talk))for index,iteminenumerate(talk):if item=='#':
            talk[index]= talk[index]+ talk[index+1]
            talk.pop(index+1)#print(talk)return talk############################################转调##########################################defzhuandiao(talk,cha):#cha = 2#初始音阶与目标音阶的差级
    targe= talkfor i,iteminenumerate(targe):if itemin syllable:
            tab= syllable.index(item)
            targe[i]= syllable[tab-cha]#print(targe)
    targe= yuchuli(''.join(targe))#print('转调后',''.join(targe))return targe###########################################转调后处理,合并括号等问题##########################################defAfterturn(targe):
    tu=0
    T=list(range(len(targe)))for index,iteminenumerate(targe):if item=='(':
            lab=-1elif item==')':
            lab=1elif item=='[':
            lab=1elif item==']':
            lab=-1else:
            lab=0

        tu= tu+ lab
        T[index]= tu#print(T)#有括号的用*字符表示,以便后面删掉for i,iteminenumerate(targe):if itemin['(',')','[',']']:
            targe[i]='*'for index,iteminenumerate(T):
        targe[index]= add_kuohao(item,targe[index])#print('加*标记的targe',targe)#删掉带有*的
    i=0while(i<len(targe)):if'*'in targe[i]:
            targe.pop(i)
            i= i-1
        i= i+1#print('删掉带有*的targe:',targe)#最后一步:合并括号#print(targe)
    T=list(range(len(targe)))for index,iteminenumerate(targe):
        a=-item.count('(')
        b= item.count('[')
        t= a+ b#计算targe中每个位置中【和(的个数,并存入T中
        targe[index]= targe[index].strip('(\|)\|[\|]')
        T[index]= t#print(targe)#将相同括号合并#print('括号索引:',T)#print("调子",targe)
    i=0while(i<(len(T)-1)):if T[i]== T[i+1]:
            targe[i]= targe[i]+ targe[i+1]
            targe.pop(i+1)
            T.pop(i+1)
            i= i-1
        i= i+1#print('去括号后的调子',targe)for index,iteminenumerate(targe):
        targe[index]= add_kuohao(int(T[index]),item)#print('最后结果:',''.join(targe))return''.join(targe)####################################################将括号分出来的函数###################################################defadd_kuohao(num,t):
    s1=''
    s2=''if num<0:for iinrange(abs(num)):
            s1= s1+'('for iinrange(abs(num)):
            s2= s2+')'if num>0:for iinrange(abs(num)):
            s1= s1+'['for iinrange(abs(num)):
            s2= s2+']'       
    t= s1+ t+ s2return tdefdealwith(talk,cha):
    talk= Pretreatment(talk)
    targe= yuchuli(talk)
    targe= zhuandiao(targe,cha)
    targe= Afterturn(targe)return targe

转调逻辑完成后,就要开始做GUI了,在GUI中,只需要调用这个东西里的dealwith函数就一步到位了。

二、GUI设计——tkinter

(一)认知

众所周知,python做GUI有很多,tkinter、wxpython便是其中之二,其中我看过一位大神用wxpython做的转调器,他的转调逻辑没我的复杂,但我的GUI界面却是学他的。

在B站容易找到tkinter的教程,我也做了少量的笔记,方便阅读,就用tkinter做个界面吧。

(二)设置思路

在这里插入图片描述
跟网页制作的思路差不多吧,我觉得,但是在python代码中挺难表达,或者说,表达起来挺复杂的。

(三)代码实现

#window.pyfrom tkinterimport*from tkinterimport ttkfrom tkinterimport scrolledtextfrom changeimport dealwithimport tkinter.fontas tf#规则和例子
txt='使用规则:1、在左边框框内输入你想要转调的简谱,再在下面选择原谱调性和目标的调性,点'\'击“开始转调”按钮进行转调,也可以“清空所有”来重置;\n\t 2、在小括号内表示降一个八度,在中'\'括号表示升一个八度,表示声一个半音就在数字前加一个#,括号不分中英文(转调后都为英文括号),'\'目前暂不支持#(123)、#[123]语义;\n\t 3、当前文本框每行最多输入60字符,超出字符自动换行,所以'\'为了美观,请自行整理谱子格式;\n\t 4、可以选中内容,在框框内进行ctrl+c和ctrl+v操作;\n\t'\' 5、由于程序问题,输入谱子时候请尽量少输入*符号和一些错误的格式,避免报错。'

lizi='''例如:《起风了》
#1(7)# 1(7)#1# 2#4# 2
#1(7)# 1(7)#1# 2#1(7# 5)
#1(7)# 1(7)#1# 2#4# 2 #1# 2#1(7)# 1
#1(7)# 1(7)#1# 2#4# 2 #1# 2#1(7# 5)
#2#1(7)# 1(7) #2#1(7)# 1(7)
(#4)#2#1(7)# 1(7)
(7)#1# 2(7) #5#4# 5 (7) #6#5#6
#6#5#6 #27【#1】7#6#5#4
#5#4# 5#4# 5#4#1# 4#2
(7)#1# 2(7) #5#4# 5 (7) #6#5#6
#6#5#6 #27【#1】7#6#5#4
~#5【#2#2】 #4# 5【#2#2】#4# 5

(7)#1# 2 #5#4 #5#4 #5#4
#1# 2 #5#4 #5#4 #5#4
#2#1(7# 5 7)#1(7# 5 7~)#23#2#1# 2
(7)#1# 2 #5#4 #5#4 #5#4
#1# 2 #5#4 #5#4 #5#4
#2#1(7# 5) #2#1(7# 5 77)
(#4# 5)#2#1(7# 5) #2#1(7# 577)

#1(7)# 1(7)#1# 2#4# 2
#1(7)# 1(7)#1# 2#1(7# 5)
#1(7)# 1(7)#1# 2#4# 2 #1# 2#1(7)# 1
#1(7)# 1(7)#1# 2#4# 2 #1# 2#1(7# 5)
#2#1(7)# 1(7) #2#1(7)# 1(7)
(#4)#2#1(7)# 1(7)

(7)#1# 2(7) #5#4# 5 (7) #6#5#6 #2
#6#5#6 #27【#1】7#6#5#4
#5#4# 5#4# 5#4#1# 4#2
(7)#1# 2(7) #5#4# 5 (7) #6#5#6 #2
#6#5#6 #27【#1】7#6#5#4
~#5【#2#2】 #4# 5【#2#2】#4# 5

(7)#1# 2 #5#4 #5#4 #5#4
#1# 2 #5#4 #5#4 #5#4
#2#1(7# 5 7)#1(7# 5 7~)#23#2#1# 2
(7)#1# 2 #5#4 #5#4 #5#4
#1# 2 #5#4 #5#4 #5#4
#2#1(7# 5) #2#1(7# 5 77)
(重复)
'''classMyWindow():def__init__(self):#########################################################################                             part one                                 ######################################################################################################       窗口设计           #############################
        win= Tk()
        win.title('转调器')
        win.geometry('1200x700')
        win.resizable(0,0)#防止用户调整尺寸

        widths=57#输入/出文本框的大小,字符数
        heights=30#行数
        ft= tf.Font(family='华文隶书',size=13)######################       开头的文字  ######################
        frame1= Frame(win)
        frame1.pack(fill=X)
        label1= Label(frame1,text=txt,justify=LEFT)
        label1.pack(side=LEFT,anchor=N)#anchor为N,靠北(上)#########################################################################                             part two                                 ###############################################################################################        输入框   #####################
        frame2= Frame(win)#第二部分:输入框和输出框
        frame2.pack(anchor=NW,padx=10,pady=10,side=LEFT)

        frame2_0= Frame(frame2)
        frame2_0.pack(side=LEFT)
        self.text_in= scrolledtext.ScrolledText(frame2_0,width=widths,
                                                 height=heights,
                                                 font=ft)#输入框,设置长和高
        self.text_in.pack(fill=X)
        self.text_in.insert(INSERT,lizi)#self.text_in.config(xscrollcommand=scroll_x1.set,yscrollcommand=scroll_y1.set)##########################        原调性选择   #########################
        Label(frame2_0,text='原调性:').pack(side=LEFT)

        number1= StringVar()
        self.cmb1= ttk.Combobox(frame2_0,width=10,textvariable=number1,
                                 state='readonly')#state设置只读
        self.cmb1.pack(side=LEFT)
        self.cmb1['values']=[u"G", u"#G/Ab", u"A", u"#A/Bb", u"B", u"C",\
                          u"#C/Db", u"D", u"#D/Eb", u"E", u"F", u"#F"]
        self.cmb1.current(5)###########################      转调\清除按钮   ##########################
        frame2_1= Frame(frame2)
        frame2_1.pack(side=LEFT)
        Button(frame2_1,text='开始转调=》',command=self.callback).pack(fill=X,padx=10)
        Button(frame2_1,text='清空所有',command=self.delete).pack(fill=X,padx=10,pady=10)######################        输出框   #####################
        frame2_2= Frame(frame2)
        frame2_2.pack(side=LEFT)
        self.text_out= scrolledtext.ScrolledText(frame2_2,width=widths,height=heights,
                                                  font=ft)
        self.text_out.pack(fill
  • 作者:墨色幽灵
  • 原文链接:https://blog.csdn.net/weixin_42953201/article/details/103103836
    更新时间:2022-10-22 11:05:30