2017-11-09 39 views
3

いくつかのTkウィジェットは、Ttkバージョンにも存在します。通常は同じ動作をしますが、インスタンスごとの外観属性(「bg」など)ではなく「スタイル」と「テーマ」を使用します。 Ttkウィジェットは、デフォルトでOSのウィンドウマネージャーの「標準的な外観」を採用しているため、外観について何も設定する必要はありません。 showvaluetickintervalreferenceを参照してください):ttk.Scaleをtk.Scaleのように動作させるには?

しかし、何らかの理由でttk.Scaleウィジェットはtk.Scaleウィジェットの2つの非常に便利なオプションを持っていません。これは、見た目よりも行動に関するものほど奇妙です。

ttkの外観を維持しながら、これらの2つのオプションを「イテレート」することは素晴らしいことです。 次のコードは、これで私の不器用な試みです。問題は、よりよい方法があるということです。 (明らかにクラス全体をカプセル化することに加えて)、半自動化された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スケールを「スライド」する前に、右に左、TTK規模でTkのスケールで、このようになります(OS /ウィンドウマネージャごとに明らかに変化するであろう):

enter image description here

+1

いくつかのアイデア:1) 'showvalue'の実装では' Scale'ウィジェットと 'Label'ウィジェットの両方に' StringVar'を単独で使うことができます。残念ながら、 'ttk.Scale'には' digits'オプションがありません。これは、値の文字列表現を制御します。とにかく、私の意見では、それは今のところOKです。 2) 'tickinterval'の実装はもっと難しいです:全ての" tick "値(必要な' tickinterval'を知っているので)、ラベルと[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:エンド -
  • widthを開始します、スケールの幅

((value - start)/extent)はパーセントで位置を与え、その後、私は、スケールの長さを掛けする必要がありますスライダの長さを考慮に入れる。 (値を示すラベルに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モジュールで利用可能です。

関連する問題