2017-11-09 7 views
3

여러 Tk 위젯은 Ttk 버전에도 존재합니다. 일반적으로 그들은 동일한 일반적인 동작을하지만 인스턴스 별 모양 속성 (예 : bg 등)보다는 "스타일"과 "테마"를 사용합니다. Ttk 위젯은 기본적으로 OS의 창 관리자의 "표준 모양"을 취하므로 외형에 대한 구성은 필요하지 않습니다. showvaluetickinterval (reference 참조)ttk.Scale을 tk.Scale처럼 동작시키는 방법?

그러나 어떤 이유로 ttk.Scale 위젯은 tk.Scale 위젯 두 가지 매우 유용한 옵션이 없습니다. 이것은 외관상보다는 행동에 대한 것이기 때문에 이상합니다.

ttk 모양을 유지하면서이 두 가지 옵션을 "적용"하는 것이 좋습니다. 다음 코드는 이에 대한 내 서투른 시도입니다. 문제는 더 좋은 방법이 있을까요? (물론 모든 것을 클래스에 캡슐화하는 것 외에) 그리고 반 자동으로 tickinterval을 어떻게 합리적으로 얻을 것인가? (아래 코드 에서처럼 "손으로"하는 것보다).

import tkinter as tk 
import tkinter.ttk as ttk 

# initial setup 
root = tk.Tk() 
frame = tk.Frame(root) 

################################################################# 
# create a tk slider showing current value and ticks 
# (showvalue=True is the default) 
tkslider = tk.Scale(frame, from_=-4, to=4, 
        orient=tk.HORIZONTAL, tickinterval=2) 
################################################################# 

################################################################# 
# create a ttk slider showing current value and ticks 
# use a ttk frame to get ttk style background 
ttkslider = ttk.Frame(frame) 
# define a callback function to update the value label 
def ttk_slider_callback(value): 
    # 'value' seems to be a string - bug or feature? 
    value_label.config(text=round(float(value))) 
    # 'text' can apparently be an int and gets converted into str 
    # (...) possibly do other stuff 
# decompose frame into two ttk labels and a ttk scale 
value_label = ttk.Label(ttkslider, text=0) 
actual_slider = ttk.Scale(ttkslider, from_=-4, to=4, 
          command=ttk_slider_callback) 
# (orient=tk.HORIZONTAL is the default) 
ticks_label = ttk.Label(ttkslider, text=' -4 -2 0 2 4 ') 
# put it all together 
value_label.grid() 
actual_slider.grid() 
ticks_label.grid() 
################################################################# 

# final setup 
tkslider.grid(row=0, column=0) 
ttkslider.grid(row=0, column=1) 
frame.grid() 
root.mainloop() 

이전 코드의 결과는 이전에 actualy (오른쪽 왼쪽에 Tk의 규모와 TTK 규모로, 다음과 같이 OS/윈도우 매니저에 따라 분명히 달라집니다, 비늘을 할 수있다 "슬라이딩")

enter image description here

+1

몇 가지 아이디어 : 1)'showvalue' 구현을 위해서 여러분은'Scale'과'Label' 위젯에 대해 하나의'StringVar'를 사용할 수 있습니다. 불행히도'ttk.Scale'은 값의 문자열 표현을 제어하는'digits' 옵션이 없습니다.어쨌든, 제 의견으로는 지금 당장은 괜찮습니다. 2)'tickinterval' 구현은 좀 더 까다 롭습니다. 원하는 tickoidval 값을 알기 때문에 모든 "tick"값을 생성 할 수 있습니다. 레이블과 [coords] (https://www.tcl.tk/man/tcl/ TkCmd/ttk_scale.htm # M23) ('tk'는'root.tk.call (actual_slider, 'coords', str (round (float (value))))')')를 호출하고이 레이블을'Scale' . – CommonSense

+0

@CommonSense 1) 게시하기 전에 인터 위젯 통신에'StringVar'를 실제로 사용하려고 시도했지만 슬프게도 실제로는 레이블 위치를 엉망으로 만드는 부동 소수점이있는 부동 소수점이 표시됩니다. 따라서 정확한 숫자를 얻기 위해 숫자를 "수동으로"반올림합니다. 2) 링크를 주셔서 감사합니다. – Dalker

답변

2
당신은 자동화 된 방식으로 배치 할 수

진드기 상기 식에 의한 place 화소에서의 위치 (x)가 (사용 값) 표시 라벨 모두 :

x = ((value - start)/extent) * (width - sliderlength) + sliderlength/2 

  • value :와

  • start 값 : 스케일의 시작점 (즉, from 옵션)
  • extent : - :, 규모

((value - start)/extent)의 폭 퍼센트의 위치를 ​​제공하고, 난 그냥 규모의 길이를 곱해야 최종

  • width 시작 그러나 슬라이더의 길이를 고려해야합니다.

    (값을 표시하는 라벨을 사용 rely=0, anchor='s')
    place(in_=self.scale, bordermode='outside', x=x, rely=1, anchor='n')
    그리고 아래의 전체 코드는 다음과 같습니다

    은 다음과 진드기를 놓습니다. 또한 digits 옵션에 대한 지원이 추가되었습니다.

    import tkinter as tk 
    import tkinter.ttk as ttk 
    
    class TtkScale(ttk.Frame): 
        def __init__(self, master=None, **kwargs): 
         ttk.Frame.__init__(self, master) 
         self.columnconfigure(0, weight=1) 
         self.showvalue = kwargs.pop('showvalue', True) 
         self.tickinterval = kwargs.pop('tickinterval', 0) 
         self.digits = kwargs.pop('digits', '0') 
    
         if 'command' in kwargs: 
          # add self.display_value to the command 
          fct = kwargs['command'] 
    
          def cmd(value): 
           fct(value) 
           self.display_value(value) 
    
          kwargs['command'] = cmd 
         else: 
          kwargs['command'] = self.display_value 
    
         self.scale = ttk.Scale(self, **kwargs) 
    
         # get slider length 
         style = ttk.Style(self) 
         style_name = kwargs.get('style', '%s.TScale' % (str(self.scale.cget('orient')).capitalize())) 
         self.sliderlength = style.lookup(style_name, 'sliderlength', default=30) 
    
         self.extent = kwargs['to'] - kwargs['from_'] 
         self.start = kwargs['from_'] 
         # showvalue 
         if self.showvalue: 
          ttk.Label(self, text=' ').grid(row=0) 
          self.label = ttk.Label(self, text='0') 
          self.label.place(in_=self.scale, bordermode='outside', x=0, y=0, anchor='s') 
          self.display_value(self.scale.get()) 
    
         self.scale.grid(row=1, sticky='ew') 
    
         # ticks 
         if self.tickinterval: 
          ttk.Label(self, text=' ').grid(row=2) 
          self.ticks = [] 
          self.ticklabels = [] 
          nb_interv = round(self.extent/self.tickinterval) 
          formatter = '{:.' + str(self.digits) + 'f}' 
          for i in range(nb_interv + 1): 
           tick = kwargs['from_'] + i * self.tickinterval 
           self.ticks.append(tick) 
           self.ticklabels.append(ttk.Label(self, text=formatter.format(tick))) 
           self.ticklabels[i].place(in_=self.scale, bordermode='outside', x=0, rely=1, anchor='n') 
          self.place_ticks() 
    
         self.scale.bind('<Configure>', self.on_configure) 
    
        def convert_to_pixels(self, value): 
         return ((value - self.start)/ self.extent) * (self.scale.winfo_width()- self.sliderlength) + self.sliderlength/2 
    
        def display_value(self, value): 
         # position (in pixel) of the center of the slider 
         x = self.convert_to_pixels(float(value)) 
         # pay attention to the borders 
         half_width = self.label.winfo_width()/2 
         if x + half_width > self.scale.winfo_width(): 
          x = self.scale.winfo_width() - half_width 
         elif x - half_width < 0: 
          x = half_width 
         self.label.place_configure(x=x) 
         formatter = '{:.' + str(self.digits) + 'f}' 
         self.label.configure(text=formatter.format(float(value))) 
    
        def place_ticks(self): 
         # first tick 
         tick = self.ticks[0] 
         label = self.ticklabels[0] 
         x = self.convert_to_pixels(tick) 
         half_width = label.winfo_width()/2 
         if x - half_width < 0: 
          x = half_width 
         label.place_configure(x=x) 
         # ticks in the middle 
         for tick, label in zip(self.ticks[1:-1], self.ticklabels[1:-1]): 
          x = self.convert_to_pixels(tick) 
          label.place_configure(x=x) 
         # last tick 
         tick = self.ticks[-1] 
         label = self.ticklabels[-1] 
         x = self.convert_to_pixels(tick) 
         half_width = label.winfo_width()/2 
         if x + half_width > self.scale.winfo_width(): 
          x = self.scale.winfo_width() - half_width 
         label.place_configure(x=x) 
    
        def on_configure(self, event): 
         """Redisplay the ticks and the label so that they adapt to the new size of the scale.""" 
         self.display_value(self.scale.get()) 
         self.place_ticks() 
    
    if __name__ == '__main__': 
        root = tk.Tk() 
        root.geometry('400x300') 
        style = ttk.Style(root) 
        style.configure('my.Horizontal.TScale', sliderlength=10) 
    
        s1 = tk.Scale(root, orient='horizontal', tickinterval=0.2, from_=-1, 
            to=1, showvalue=True, resolution=0.1, sliderlength=10) 
        s2 = TtkScale(root, style='my.Horizontal.TScale', orient='horizontal', 
            tickinterval=0.2, from_=-1, to=1, showvalue=True, 
            digits=1) 
    
        ttk.Label(root, text='tk.Scale').pack() 
        s1.pack(fill='x') 
        ttk.Label(root, text='ttk.Scale').pack() 
        s2.pack(fill='x') 
    
        root.mainloop() 
    

    screenshot

    이 위젯의 ​​더 완전한 버전은 이름 TickScale에서 ttkwidgets 모듈에서 사용할 수 있습니다.