免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5151 | 回复: 13
打印 上一主题 下一主题

新定义的控件怎么捕捉不到方向键操作? [复制链接]

论坛徽章:
4
金牛座
日期:2013-10-11 16:12:50卯兔
日期:2014-07-31 09:17:19辰龙
日期:2014-08-08 09:28:02狮子座
日期:2014-09-14 20:32:05
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-01-13 22:39 |只看该作者 |倒序浏览
20可用积分
本帖最后由 ssfjhh 于 2013-01-16 00:17 编辑

我需要一个python3下可用的多列的列表,在论坛里发过贴,得到的答案有tkintertable,但是却是for python2 only的,我没有办法使用。

后在网上搜索到别人写的一个控件multilistbox,用在python2下,经过修改现在它可以用在python3下了,只是不知道为什么这个控件不能捕捉方向键,还有一个文本框跟这个multilistbox都在一个窗口中,我一按方向键,发现文本框中的光标在移动,这个multilistbox却不能更改当前所选项,不知道什么地方定义的不对,或者没有定义,急求高手解答,我的软件的其它部分均已经完成,卡在这里了。
  1. class MultiListbox(Frame):
  2.     def __init__(self, master, lists):
  3.         Frame.__init__(self, master)
  4.         self.lists = []
  5.         for l,w in lists:
  6.             frame = Frame(self); frame.pack(side=LEFT, expand=YES, fill=BOTH)
  7.             Label(frame, text=l, borderwidth=1, relief=RAISED).pack(fill=X)
  8.             lb = Listbox(frame, width=w, borderwidth=0, selectborderwidth=0,
  9.              relief=FLAT, exportselection=FALSE)
  10.             lb.pack(expand=YES, fill=BOTH)
  11.             self.lists.append(lb)
  12.             lb.bind('<B1-Motion>', lambda e, s=self: s._select(e.y))
  13.             lb.bind('<Button-1>', lambda e, s=self: s._select(e.y))
  14.             lb.bind('<Leave>', lambda e: 'break')
  15.             lb.bind('<B2-Motion>', lambda e, s=self: s._b2motion(e.x, e.y))
  16.             lb.bind('<Button-2>', lambda e, s=self: s._button2(e.x, e.y))
  17.         self.bind('<Up>', lambda e, s=self: s._scroll(SCROLL, 1, PAGES))
  18.         self.bind('<Down>', lambda e, s=self: s._scroll(SCROLL, -1, PAGES))
  19.             
  20.         frame = Frame(self); frame.pack(side=LEFT, fill=Y)
  21.         Label(frame, borderwidth=1, relief=RAISED).pack(fill=X)
  22.         sb = Scrollbar(frame, orient=VERTICAL, command=self._scroll)
  23.         sb.pack(expand=YES, fill=Y)
  24.         self.lists[0]['yscrollcommand']=sb.set

  25.     def Onchooseup(self):
  26.         newselection = self.curselection + 1
  27.         self.selection_clear()
  28.         self.selection_set(newselection)

  29.     def Onchoosedown(self):
  30.         newselection = self.curselection - 1
  31.         self.selection_clear()
  32.         self.selection_set(newselection)

  33.     def _select(self, y):
  34.         row = self.lists[0].nearest(y)
  35.         self.selection_clear(0, END)
  36.         self.selection_set(row)
  37.         self.focus_force()
  38.         return 'break'

  39.     def _button2(self, x, y):
  40.         for l in self.lists:
  41.             l.scan_mark(x, y)
  42.         return 'break'

  43.     def _b2motion(self, x, y):
  44.         for l in self.lists:
  45.             l.scan_dragto(x, y)
  46.         return 'break'

  47.     def _scroll(self, *args):
  48.         for l in self.lists:
  49.             l.yview(*args)
  50.         return 'break'

  51.     def curselection(self):
  52.         return self.lists[0].curselection()

  53.     def itemconfigure(self, row_index, col_index, cnf=None, **kw):
  54.         lb = self.lists[col_index]
  55.         return lb.itemconfigure(row_index, cnf, **kw)

  56.     def rowconfigure(self, row_index, cnf={}, **kw):
  57.         for lb in self.lists:
  58.             lb.itemconfigure(row_index, cnf, **kw)

  59.     def delete(self, first, last=None):
  60.         for l in self.lists:
  61.             l.delete(first, last)

  62.     def get(self, first, last=None):
  63.         result = []
  64.         for l in self.lists:
  65.             result.append(l.get(first,last))
  66.         #if last:
  67.         #    return map(*([None] + result))
  68.         return result

  69.     def index(self, index):
  70.         self.lists[0].index(index)

  71.     def insert(self, index, *elements):
  72.         for e in elements:
  73.             i = 0
  74.         for l in self.lists:
  75.             l.insert(index, e[i])
  76.             i = i + 1

  77.     def size(self):
  78.         return self.lists[0].size()

  79.     def see(self, index):
  80.         for l in self.lists:
  81.             l.see(index)

  82.     def selection_anchor(self, index):
  83.         for l in self.lists:
  84.                l.selection_anchor(index)

  85.     def selection_clear(self, first, last=None):
  86.         for l in self.lists:
  87.             l.selection_clear(first, last)

  88.     def selection_includes(self, index):
  89.         return self.lists[0].selection_includes(index)

  90.     def selection_set(self, first, last=None):
  91.         for l in self.lists:
  92.             l.selection_set(first, last)

  93.     def yview_scroll(self, *args, **kwargs):
  94.         for lb in self.lists:
  95.             lb.yview_scroll(*args, **kwargs)
复制代码

最佳答案

查看完整内容

http://tkinter.unpythonic.net/wiki/mhMultiListBox这里这个合适你用,功能齐备,也不会太过庞大,也是2.X的,不过使用Tools/Scripts/2to3.py转换一下就行了,我试了,可行。在命令行中执行:python.exe c:\python32\tools\scripts\2to3.py -w mhMultiListbox.py如果要使用ttk,将字体、颜色、样式等控件设置移动到style参数里面就行,参考VisualTkinter生成的代码。另,这里这个也可以试一下,我没有试:http://mail.python.org/ ...

论坛徽章:
0
2 [报告]
发表于 2013-01-13 22:39 |只看该作者
http://tkinter.unpythonic.net/wiki/mhMultiListBox
这里这个合适你用,功能齐备,也不会太过庞大,也是2.X的,不过使用Tools/Scripts/2to3.py转换一下就行了,我试了,可行。

在命令行中执行:
python.exe c:\python32\tools\scripts\2to3.py -w mhMultiListbox.py

如果要使用ttk,将字体、颜色、样式等控件设置移动到style参数里面就行,参考VisualTkinter生成的代码。


另,这里这个也可以试一下,我没有试:
http://mail.python.org/pipermail ... ptember/002069.html

反正对于普通不是很大的代码量来说,2to3.py脚本的处理结果还是可以接受的,剩余的一点无法自动转换的,手工修改一下下就行了。

论坛徽章:
0
3 [报告]
发表于 2013-01-15 21:22 |只看该作者
想捕捉方向键,当然得绑定方向键事件,比如:
self.bind('<KeyRelease>', self.keyrelease)

然后在回调函数中自己处理,根据event.keysym字符串的值就可以知道按的是什么键了,比如:"Up"/"Down"等。

论坛徽章:
4
金牛座
日期:2013-10-11 16:12:50卯兔
日期:2014-07-31 09:17:19辰龙
日期:2014-08-08 09:28:02狮子座
日期:2014-09-14 20:32:05
4 [报告]
发表于 2013-01-15 21:41 |只看该作者
回复 2# cdhigh


    我已经写了,可是没有用,光标只是在左侧的文本框里移动。这个控件缺少太多东西了。

论坛徽章:
0
5 [报告]
发表于 2013-01-15 21:47 |只看该作者
贴出你写的吧

论坛徽章:
4
金牛座
日期:2013-10-11 16:12:50卯兔
日期:2014-07-31 09:17:19辰龙
日期:2014-08-08 09:28:02狮子座
日期:2014-09-14 20:32:05
6 [报告]
发表于 2013-01-15 23:37 |只看该作者
本帖最后由 ssfjhh 于 2013-01-16 00:57 编辑
  1. self.bind('<Up>', self.Onchooseup)
  2. self.bind('<Down>', self.Onchoosedown)

  3. def Onchooseup(self)
  4.     newselection = self.curselection + 1
  5.     self.selection_clear()
  6.     self.selection_set(newselection)

  7. def Onchoosedown(self)
  8.     newselection = self.curselection - 1
  9.     self.selection_clear()
  10.     self.selection_set(newselection)
复制代码
我把这些代码添加到了这个类中,没有反应。

修改后的完整代码见一楼。

论坛徽章:
4
金牛座
日期:2013-10-11 16:12:50卯兔
日期:2014-07-31 09:17:19辰龙
日期:2014-08-08 09:28:02狮子座
日期:2014-09-14 20:32:05
7 [报告]
发表于 2013-01-16 00:40 |只看该作者
回复 4# cdhigh

附件是一个叫nltk的包里边定义的MultiListbox,但是这个包是for python2.x only的,所以我已经把这个MultiListbox拎出来改成for python3的

但是总出错,能不能帮改下?

    mul.zip (5.22 KB, 下载次数: 12)

论坛徽章:
0
8 [报告]
发表于 2013-01-16 02:25 |只看该作者
单独改这个问题倒是很简单,绑定键盘事件到LISTBOX就行了,
lb.bind('<Up>', lambda e: self.select(delta=-1))
lb.bind('<Down>', lambda e: self.select(delta=1))
lb.bind('<Prior>', lambda e: self.select(delta=-self._pagesize()))
lb.bind('<Next>', lambda e: self.select(delta=self._pagesize()))

上下箭头键的处理正常,
难得是翻页键的处理,我试了一下,会出现各个LISTBOX的行不同步的情况,
比如先点一下鼠标选择一行,然后按PageDown,会出现点击鼠标的那个LISTBOX的行比其他LISTBOX往上多滚了一行情况。
不知何解,没有时间细细研究了。

论坛徽章:
4
金牛座
日期:2013-10-11 16:12:50卯兔
日期:2014-07-31 09:17:19辰龙
日期:2014-08-08 09:28:02狮子座
日期:2014-09-14 20:32:05
9 [报告]
发表于 2013-01-16 09:56 |只看该作者
回复 7# cdhigh



    这个方法我没有试,是我写的那个onchoose方法不对,根本就不用新写一个方法,让你见笑了。

不过,你在8楼给出的第一个方法中有个_move方法,可以用来翻页,我试了下,没有任何问题。如果想学习,可以看下它的代码。我现在也没有功夫细研究了,能用就行。

另外8楼给出的方法,跟我的那个multilistbox代码很一致,我猜想,他们某一方可能copy过对方或者第三方的代码,互相又都少了些方法,我把这个_move方法copy到我的代码中,现在已经可以正常使用了。

万分感谢。

论坛徽章:
4
金牛座
日期:2013-10-11 16:12:50卯兔
日期:2014-07-31 09:17:19辰龙
日期:2014-08-08 09:28:02狮子座
日期:2014-09-14 20:32:05
10 [报告]
发表于 2013-01-16 10:21 |只看该作者
本帖最后由 ssfjhh 于 2013-01-16 10:34 编辑

回复 8# cdhigh


    http://mail.python.org/pipermail ... ptember/002069.html

后面一个table也不错,用2to3.py脚本转换后不能直接运行。
我给改好了。放在这里,给需要的人。
  1. from tkinter import *
  2. from tkinter.font import Font
  3. from math import floor

  4. class Spreadsheet(Frame):

  5.     def __init__(self, parent, font=None, **keywords):
  6.         Frame.__init__(self, parent)
  7.         self.columns=[]

  8.         # Setup font information
  9.         if font:
  10.             self.txtFont=font
  11.         else:
  12.             self.txtFont=Font(family="Arial", size=14)
  13.         self.defaultRowHeight=self.txtFont['size']+7

  14.         self.headerFont=self.txtFont.copy()
  15.         self.headerFont.configure(weight='bold')

  16.         self.spreadsheet=Canvas(self, bg='white', bd=0, highlightthickness=0, **keywords)
  17.         self.header=Canvas(self, height=self.defaultRowHeight+2, bd=0, highlightthickness=0, bg='white')
  18.         self.header.pack(side=TOP, expand=FALSE, fill=X, pady=0)

  19.         self.scrollY = Scrollbar(self, orient=VERTICAL, command=self.spreadsheet.yview )
  20.         self.scrollX = Scrollbar(self, orient=HORIZONTAL, command=self.xview )
  21.         self.spreadsheet["xscrollcommand"] = self.scrollX.set
  22.         self.spreadsheet["yscrollcommand"] = self.scrollY.set
  23.         self.scrollY.pack(side="right", fill="y")
  24.         self.scrollX.pack(side="bottom", fill="x")
  25.         self.spreadsheet.pack(fill="both", expand=True, side="left")

  26.         # Set up the mousewheel to scroll
  27.         self.spreadsheet.focus_set()
  28.         self.spreadsheet.bind("<MouseWheel>", self.mouseScroll)

  29.         # Store current cursor (the one to restore to after a change)
  30.         self.defaultCursor=self.cget("cursor")

  31.         self.bind("<Configure>", self.catchResize)

  32.     def catchResize(self, event):
  33.         try:
  34.             self.after_cancel(self.config_id)
  35.         except: pass
  36.         self.config_id = self.after(500, self.optimiseColumns)


  37.     def xview(self, *args):
  38.         self.header.xview(*args)
  39.         self.spreadsheet.xview(*args)

  40.     def initialise(self):

  41.         self.spreadsheet.delete(ALL)

  42.         # Any window items still bound, destroy
  43.         for c in self.spreadsheet.children.values(): c.destroy()

  44.         self.columns=[]
  45.         self.rows=[]
  46.         self.startCol=0
  47.         self.startRow=0
  48.         self.totalHeight=0


  49.     def mouseScroll(self, event):
  50.         if event.delta >0:
  51.             self.spreadsheet.yview("scroll", "-1", "units")
  52.         else:
  53.             self.spreadsheet.yview("scroll", "1", "units")


  54.     def setupColumns(self, columns):

  55.         self.columns=[]

  56.         for i in range(0, len(columns)):
  57.             column=columns[i]
  58.             name=column[0]
  59.             width=column[1]
  60.             if len(column)>2:
  61.                 align=column[2]
  62.             else:
  63.                 align=CENTER
  64.             self.columns.append([name,width,align])


  65.     def addColumn(self, label, width=50, align=LEFT, bg='white', fg='black'):
  66.         col=dict()
  67.         col['label']=label
  68.         col['width']=width
  69.         col['align']=align
  70.         col['bg']=bg
  71.         col['fg']=fg
  72.         self.columns.append(col)

  73.     def addRow(self, pos, row):
  74.         row=Row(row)
  75.         row.height=self.getRowHeight(row)

  76.         row.widgets=[]
  77.         col=0
  78.         for item in row:
  79.             colDat=self.columns[col]
  80.             if isinstance(item, Widget):
  81.                 row.widgets.append(item)
  82.                 item.internal=False
  83.             else:
  84.                 e=Entry(self.spreadsheet, bg=colDat['bg'], fg=colDat['fg'], font=self.txtFont, justify=colDat['align'])
  85.                 e.internal=True
  86.                 e.insert(END, item)
  87.                 if colDat['align']==RIGHT:
  88.                     e.xview(END)
  89.                 row.widgets.append(e)
  90.             col += 1

  91.         if pos==END:
  92.             self.rows.append(row)
  93.         else:
  94.             self.rows.insert(pos, row)

  95.     def getRowHeight(self, row):
  96.         maxh=0
  97.         for item in row:
  98.             maxh=max(maxh, self.valHeight(item))
  99.         return maxh


  100.     def optimiseColumns(self, fixedWidth=True):
  101.         if not self.columns: return

  102.         # 1. Find the current total
  103.         totWidth=0
  104.         for column in self.columns:
  105.             totWidth+=column['width']

  106.         # Minimise columns which can be
  107.         newWidth=0
  108.         for col in range(0, len(self.columns)):
  109.             maxwidth=self.neededWidth(col)
  110.             colObj=self.columns[col]
  111.             if maxwidth<colObj['width']:
  112.                 colObj['width']=maxwidth
  113.             newWidth+=colObj['width']

  114.         # Now, if some columns need more space, and it is available, give it to them
  115.         swidth=self.spreadsheet.winfo_width()
  116.         if swidth<2:
  117.             swidth=self.spreadsheet.winfo_reqwidth()

  118.         if swidth>newWidth:
  119.             # we have free space

  120.             expand=[]
  121.             for col in range(0, len(self.columns)):
  122.                 coldat=self.columns[col]
  123.                 reqwidth=self.neededWidth(col)
  124.                 if reqwidth>coldat['width']:
  125.                     expand.append((coldat, reqwidth-coldat['width']))

  126.             # Now, we assign each col an equal share of the free space,
  127.             # up to their max requirement
  128.             free=swidth-newWidth
  129.             expand.sort(key =lambda e: e[1])
  130.             while expand:
  131.                 if free<1: break
  132.                 col,req=expand.pop()
  133.                 req=min(free, req)
  134.                 col['width']+=req
  135.                 free=free-req

  136.         self.show()

  137.     def neededWidth(self, col):
  138.         maxwidth=self.headerFont.measure(self.columns[col]['label'])
  139.         for row in self.rows:
  140.             wdth=self.valWidth(row[col])
  141.             maxwidth=max(wdth, maxwidth)
  142.         return maxwidth+6


  143.     def valWidth(self, val):
  144.         if isinstance(val, str):
  145.             return self.txtFont.measure(val)
  146.         try:
  147.             return val.winfo_reqwidth()
  148.         except: pass


  149.     def valHeight(self, val):
  150.         if isinstance(val, str):
  151.             return self.defaultRowHeight
  152.         try:
  153.             return val.winfo_reqheight()
  154.         except: pass


  155.     ##########################################
  156.     # REDRAWING

  157.     # REDRAW after change of screensize
  158.     # Called after screen resize or the first time

  159.     def show(self):
  160.         self.spreadsheet.delete(ALL)
  161.         self.redrawHeader()
  162.         self.redrawSheet()

  163.     def redrawHeader(self):
  164.         self.header.delete(ALL)
  165.         x=5
  166.         height=self.defaultRowHeight+2
  167.         self.header.create_line(x, 2, x, height)
  168.         count=0
  169.         for col in self.columns:
  170.             width=col['width']
  171.             self.header.create_rectangle(x+1, 3, x+width-1, height-1, fill="#c1c2ef", outline="#c1c2ef")
  172.             self.header.create_line(x, 2, x+width, 2)
  173.             self.header.create_line(x, height, x+width, height)
  174.             self.header.create_line(x+width, 2, x+width, height, tags=('colend', str(count)))

  175.             self.header.create_text(x+width/2, 1, text=col['label'], anchor=N, font=self.headerFont, tags=('title', str(count)))
  176.             # for the endline use a rect of width 3, but the border same as background
  177.             # Basically, this gives us a widget of 3 pix width for detecting enter/leave events
  178.             self.header.create_rectangle(x+width-1, 2, x+width+1,height, fill='black', outline="#c1c2ef", tags=('colend', str(count)))
  179.             x+=width
  180.             count+=1

  181.         self.header["scrollregion"]=(0,0, x+self.scrollY.winfo_reqwidth(), height)

  182.         # Make sure all controls are on top
  183.         self.header.tag_raise('colend')
  184.         self.header.tag_bind("colend", "<Enter>", self.enterColEnd)
  185.         self.header.tag_bind("colend", "<Leave>", self.leaveColEnd)
  186.         self.header.tag_bind("colend", "<Button-1>", self.startDrag)
  187.         self.header.tag_bind("title", "<ButtonRelease>", self.orderByColumn)

  188.     def redrawSheet(self):
  189.         if not self.rows: return
  190.         # now show the data
  191.         y=0
  192.         for row in self.rows:
  193.             height=row.height
  194.             x=5
  195.             col=-1
  196.             for value in row.widgets:
  197.                 col+=1
  198.                 width=self.columns[col]['width']
  199.                 self.drawCell(x, y, width, height, value, self.columns[col])
  200.                 x+=width
  201.             y+=height

  202.         self.spreadsheet["scrollregion"]=(0,0, x, y)

  203.     def orderByColumn(self, event):
  204.         wgt=self.header.find_withtag('current')
  205.         colID=int(self.header.gettags(wgt)[1])
  206.         self.sortOnColumn(colID)

  207.     def enterColEnd(self, event):
  208.         self.header.configure(cursor='sb_h_double_arrow')

  209.     def leaveColEnd(self, event):
  210.         self.header.configure(cursor=self.defaultCursor)

  211.     def startDrag(self, event):
  212.         self.wgt1=self.header.find_withtag('current')
  213.         self.currCol=self.columns[int(self.header.gettags(self.wgt1)[1])]
  214.         self.startX=self.header.bbox(self.wgt1)[0]
  215.         self.header.bind('<B1-Motion>', self.moveBorder)
  216.         self.header.bind('<ButtonRelease>', self.stopMoveBorder)

  217.     def moveBorder(self, event):
  218.         self.header.tag_raise(self.wgt1)
  219.         wgt_x=self.header.bbox(self.wgt1)[0]
  220.         diff=event.x-wgt_x
  221.         self.header.move(self.wgt1, diff, 0)

  222.     def stopMoveBorder(self, event):
  223.         self.header.unbind('<B1-Motion>')
  224.         self.header.unbind('<ButtonRelease>')
  225.         self.grab_release()
  226.         wgt_x=self.header.bbox(self.wgt1)[0]
  227.         change=wgt_x-self.startX
  228.         self.currCol['width']+=change
  229.         self.show()

  230.     def drawCell(self, x, y, width, height, value, col):
  231.         if value.internal:
  232.             self.spreadsheet.create_window(x, y,  window=value,
  233. height=height, width=width, anchor=NW)
  234.         else:
  235.             wheight=min(height-2,value.winfo_reqheight())
  236.             self.spreadsheet.create_window(x+width/2, y+height/2,
  237. window=value, height=wheight, anchor=CENTER)

  238.     def sortOnColumn(self, colID):
  239.         self.rows.sort(key =lambda a, c=colID: a[c])
  240.         self.show()


  241. class Row(list):

  242.     def __init__(self, vals):
  243.         if isinstance(vals, tuple): vals=list(vals)
  244.         list.__init__(self, vals)
  245.         self.height=0



  246. if __name__ == '__main__':
  247.     tk = Tk()
  248.     ssw = Spreadsheet(tk, width=900)
  249.     ssw.pack(side=TOP, expand=TRUE, fill=BOTH)
  250.     ssw.initialise()
  251.     ssw.addColumn('', 110)
  252.     ssw.addColumn('Subject', 300, bg='light blue')
  253.     ssw.addColumn('Sender', 200, align=CENTER)
  254.     ssw.addColumn('Date', 200, align=RIGHT)

  255.     # if embedded widgets do not have ssw.spreadsheet as parent, they will overlap the border
  256.     for i in range(0,20):
  257.         but=Button(ssw.spreadsheet, text="View", font="Arial 8")
  258.         ssw.addRow(END, (but, 'Important Message: %d' % i, 'John Doe',
  259. '10/10/%04d' % (1900-i)))

  260.     ssw.optimiseColumns()
  261.     ssw.show()
  262.     tk.mainloop()
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP