Ruby/Tk: how to get smaller button widget with image - ruby

I'm coding on Tk 8.5.9 from ActiveTcl on Ruby 1.8.7 on a Mac OS X 10.6.
To meet my application requirements I need to make the button widgets as small as the gif image but I am not able to. I have been hours searching and experimenting with negative results.
Greatly thankful in advance for any clues.
Following is the code i am trying to get small buttons from.
require 'tk'
require 'tkextlib/tile'
$up_img = TkPhotoImage.new("file"=>"arrowup-n.gif")
$down_img = TkPhotoImage.new("file"=>"arrowdown-n.gif")
root = TkRoot.new {title "Ek Composer"}
content = Tk::Tile::Frame.new(root).pack
Tk::Tile::Button.new(content) {width 1;image $up_img; command {move_up} }.pack
Tk::Tile::Button.new(content) {width 1;image $down_img;command {move_down}}.pack
def move_up
p "move up"
end
def move_down
p "move down"
end
Tk.mainloop
But the buttons remain too big :(.

It's awkward. The OSX theme really wants to add extra space at either end of the button.
You can try switching to the classic button (in tk itself) but that puts more space vertically and looks a bit less native. Or you can put the image in a label (which you can shrink exactly) and add bindings to it to make it respond to mouse clicks.

I added binding to label. Works fine. Thanks. Follows a code snippet of label with binding as button.
require 'tk'
$resultsVar = TkVariable.new
root = TkRoot.new
root.title = "Window"
$up_img = TkPhotoImage.new("file"=>"arrowup-n.gif")
$down_img = TkPhotoImage.new("file"=>"arrowdown-n.gif")
Lbl = TkLabel.new(root) do
image $up_img
borderwidth 0
font TkFont.new('times 20 bold')
foreground "red"
relief "groove"
pack("side" => "right", "padx"=> "50", "pady"=> "50")
end
Lbl.bind("ButtonPress-1") {
Lbl.configure("image"=>$down_img)
}
Lbl.bind("ButtonRelease-1") {
Lbl.configure("image"=>$up_img)
}
Lbl['textvariable'] = $resultsVar
$resultsVar.value = 'New value to display'
Tk.mainloop

Related

Two colors in one string

Hi I'm trying to do a GUI code using GTK in Ruby and I'm stuck trying to change the color of a String.
I would like the Welcome to be blue and the #name to be red but I can't seem to figure out a way to get both of them
#user = Gtk::Label.new("Welcome #{#name}")
css_user = Gtk::CssProvider.new
css_user.load(data: "label{color: blue;}")
If anybody could help I would be really greatful
I had to change a little bit the gtk display
#box = Gtk::Box.new(:horizontal, 1)
#welcome = Gtk::Label.new("Welcome ")
#user = Gtk::Label.new(#usuari)
css_user = Gtk::CssProvider.new
css_user.load(data: "label{color: blue;}")
css_welcome = Gtk::CssProvider.new
css_welcome.load(data: "label{color: black;}")
#user.style_context.add_provider(css_user, Gtk::StyleProvider::PRIORITY_USER)
#welcome.style_context.add_provider(css_welcome, Gtk::StyleProvider::PRIORITY_USER)
As you can see I created two labels (one for each color) and I placed them inside a Horizontal Box

How to set the gravity on a GTK3+ window in python

I run python 2.7.13 on windows 7.
I am creating a window with Gtk (from pygobject 3.18.2).
I am running windows 7 with a custom shell and I am trying to make a toolbar at the bottom of the screen.
I use a grid to divide the window in a top and a bottom part.
The bottom part is always visible.
The top part must show above the bottom part on mouse enter and hide on mouse leave without moving the bottom part.
The default positioning of a window uses the top-left corner of the window, but this will cause the bottom part to shift up to the position of the top part when the top part is hidden.
I think I understand that I have to use
set_gravity(Gdk.Gravity.SOUTH_WEST)
to change this behaviour
I do not get errors, but it seems this setting is ignored. The placement of the window is not affected at all.
What am I missing?
Anything wrong in the way I call set_gravity()?
Is set_gravity the right way to achieve this?
I read Set window gravity in PyGObject?, but this question is still not answered
Here is the code I try to get working
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Test")
self.set_decorated(0)
self.screen = Gdk.Screen.get_default()
self.connect("destroy", self.destroy)
self.connect("enter-notify-event", self.mouseenter)
self.connect("leave-notify-event", self.mouseleave)
self.label1 = Gtk.Label("Label1\n line1\n line2")
self.label2 = Gtk.Label("Label2")
self.label1.set_hexpand(True)
self.label2.set_hexpand(True)
self.maingrid = Gtk.Grid()
self.add(self.maingrid)
self.maingrid.attach(self.label1, 0, 0, 1, 1)
self.maingrid.attach(self.label2, 0, 1, 1, 1)
self.set_gravity(Gdk.Gravity.SOUTH_WEST) # looks like this is ignored
print self.get_gravity()
def mouseleave(self, widget, data=None):
print "mouse leave"
self.label1.hide()
label2_height = self.label2.get_allocation().height
self.resize(self.screen.width(), label2_height)
def mouseenter(self, widget, data=None):
print "mouse enter"
label1_height = self.label1.get_allocation().height
label2_height = self.label2.get_allocation().height
self.resize(self.screen.width(), label1_height + label2_height)
self.label1.show()
# Here I expect label2 to stay where it is at the bottom of the screen and label1 to be drawn above label2.
# But label2 is pushed down to make space for label1
# (normal behaviour if Gdk.Gravity.SOUTH_WEST is not set)
def destroy(self, widget, data=None):
print "destroy signal occurred"
Gtk.main_quit()
win = MyWindow()
win.show_all()
win.label1.hide()
height = win.label2.get_allocation().height
win.resize(win.screen.width(), height)
#win.move(0, win.screen.height()) # I expect this to place the window at the bottom of the screen
# if Gdk.Gravity.SOUTH_WEST is set, but it is placed offscreen
# (normal behaviour if Gdk.Gravity.SOUTH_WEST is not set)
win.move(0, win.screen.height() - 200) # shift it up 200 pixels to see what is happening
Gtk.main()
Here is a working version where I move the window to it's proper position after resizing. Moving the window makes the window flicker and it also generates the leave-notify-event and the enter-notify-event.
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Test")
self.set_decorated(0)
self.screen = Gdk.Screen.get_default()
# self.set_gravity(Gdk.Gravity.SOUTH_WEST)
self.connect("destroy", self.destroy)
self.connect("enter-notify-event", self.mouseenter)
self.connect("leave-notify-event", self.mouseleave)
self.label1 = Gtk.Label("Label1\n line1\n line2")
self.label2 = Gtk.Label("Label2")
self.label1.set_hexpand(True)
self.label2.set_hexpand(True)
self.maingrid = Gtk.Grid()
self.add(self.maingrid)
self.maingrid.attach(self.label1, 0, 0, 1, 1)
self.maingrid.attach(self.label2, 0, 1, 1, 1)
self.ismoving = 0
def mouseleave(self, widget, data=None):
print "mouse leave"
if self.ismoving:
print "window is moving"
else:
self.label1.hide()
label2_height = self.label2.get_allocation().height
self.resize(self.screen.width(), label2_height)
self.move(0, self.screen.height() - label2_height)
def mouseenter(self, widget, data=None):
print "mouse enter"
if self.ismoving: # moving the window generates a leave-notify-event and a enter-notify-event
self.ismoving = 0 # ignore these events when moving the window
else:
self.ismoving = 1
label1_height = self.label1.get_allocation().height
label2_height = self.label2.get_allocation().height
self.resize(self.screen.width(), label1_height + label2_height)
self.move(0, self.screen.height()-label1_height - label2_height)
self.label1.show()
def destroy(self, widget, data=None):
print "destroy signal occurred"
Gtk.main_quit()
win = MyWindow()
win.show_all()
win.label1.hide()
height = win.label2.get_allocation().height
win.resize(win.screen.width(), height)
win.move(0, win.screen.height() - height)
Gtk.main()
Based on AlexB's comment i assume my code is correct, but it is not working for me. I don't see any reason why it will not run under python 2. Maybe there is an issue with the window manager. I'll investigate
Did anyone succesfully use set_gravity() on windows?
Documentation indicates it may or may not work, depending on Window Manager. It doesn't for me on Xubuntu 18.04

scrollable window ncurses ruby

I created a class that I am trying to make to simulate richtextbox, sort of, on windows forms. this means when you add new data to the form/richtextbox it is added to the bottom of the box/window and the rest is scrolled up one line.
ive tried to enable scrollok(), but it does not seem to want to scroll. i am not sure if it's bugged or my way of implementing it is wrong.
class Textpad
attr_accessor :data, :name, :window
def initialize(name, height, width, startx, starty)
#data = []
#name = name
#height = height
#width = width
#startx = startx
#starty = starty
Ncurses.refresh
#window = Ncurses.newwin(height, width, starty, startx)
#window.scrollok true
#window.wrefresh
end
def add(packetid, username, message)
#data.push [Time.new.strftime('[%T]'), packetid, username, message]
#data.shift if #data.length > 500
end
def draw
Ncurses.init_pair(1, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK)
Ncurses.init_pair(2, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK)
Ncurses.init_pair(3, Ncurses::COLOR_RED, Ncurses::COLOR_BLACK)
Ncurses.init_pair(4, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK)
#window.wclear
position = 0
#data.each do |timestamp, packetid, username, message|
case packetid
when '1005'
#window.mvwprintw(1*position, 1, "#{timestamp} «#{username}» #{message}")
#window.mvchgat(1*position, timestamp.length+2, 1, Ncurses::A_NORMAL, 3, NIL)
#window.mvchgat(1*position, timestamp.length+3+username.length, 1, Ncurses::A_NORMAL, 3, NIL) #colorize the symboles around the username
end
position += 1
end
#window.wrefresh
end
end
the problem would be inside my draw method of the Textpad class. i can fill the data array for the Textpad class with hundreds of entries but only the very top of the array gets written (until it reaches the bottom of the window) with no scrolling. Do i manually have to scroll the screen or something? from the documentation it says it should automatically scroll when the cursor reaches the bottom and another line is added.
To quote the man page:
The scrollok option controls what happens when the cursor of a window is moved off the edge of the window or scrolling region, either as a result of a newline action on the bottom line, or typing the last character of the last line. If disabled, (bf is FALSE), the cursor is left on the bottom line. If enabled, (bf is TRUE), the window is scrolled up one line...
What is happening in your code is that you are attempting to print outside the window which is an error, but an error that curses handles by not printing anything.
You can either print an new line when you get to the bottom of the window or once your reach the bottom of the window you can call #window.scroll.
Either way you will need to keep printing on the last line if you are explicitly setting the position.

Menu bar icon in OS X for script running as daemon?

I have a ruby script(https://github.com/daemonza/MacBak) that runs on my macbook as a daemon and monitors a bunch of directories for file changes and rsync any changes that happens. I was wondering would i be able to let it create a icon in the menu bar at the top? Just so that I know it's actually running, without having to check for it with ps.
Maybe later if needed I might want to be able to control the script from there, simple drop down with stop and status entries, etc.
It seems from ObjectC I can call NSStatusItem to get the icon, but I really just want to do it easily from my Ruby script. Perhaps maybe some applescript call that I can do?
This MacRuby script creates a status bar icon:
https://github.com/ashchan/gmail-notifr
So does this one:
https://github.com/isaac/Stopwatch
Here's a Gist including code that does it:
https://gist.github.com/1480884
# We build the status bar item menu
def setupMenu
menu = NSMenu.new
menu.initWithTitle 'FooApp'
mi = NSMenuItem.new
mi.title = 'Hellow from MacRuby!'
mi.action = 'sayHello:'
mi.target = self
menu.addItem mi
mi = NSMenuItem.new
mi.title = 'Quit'
mi.action = 'quit:'
mi.target = self
menu.addItem mi
menu
end
# Init the status bar
def initStatusBar(menu)
status_bar = NSStatusBar.systemStatusBar
status_item = status_bar.statusItemWithLength(NSVariableStatusItemLength)
status_item.setMenu menu
img = NSImage.new.initWithContentsOfFile 'macruby_logo.png'
status_item.setImage(img)
end
# Menu Item Actions
def sayHello(sender)
alert = NSAlert.new
alert.messageText = 'This is MacRuby Status Bar Application'
alert.informativeText = 'Cool, huh?'
alert.alertStyle = NSInformationalAlertStyle
alert.addButtonWithTitle("Yeah!")
response = alert.runModal
end
def quit(sender)
app = NSApplication.sharedApplication
app.terminate(self)
end
app = NSApplication.sharedApplication
initStatusBar(setupMenu)
app.run
You could look at MacRuby. It's a way of developing OS X apps using Ruby instead of Objective-C. It includes a number of improvements, such as getting rid of header files, so yu just have "implementation" files in Ruby. You can use IB for building windows too

Python 3.1 Tkinter layout help. I am close, please help me finish this

I am using Python 3.1 by the way.
I am trying to build a simple GUI using Tkinter - label, text entry field, button on the first row and editable text area with scrollbar to the right and on the bottom of it - on the second row. Please help me fix up the layout. What I have below does not quite work. If I have to use a grid, I will. I wish to keep the code very simple - I want to "sell" Python to some of my coworkers. So, I want to get a somewhat decent look and feel. Suggest better padding if you do not mind. Also, if my variable names, etc. seem weird, then please make a note.
At the same time I want to pretend that this is a throw-away script which I have not spent much time on. Since I am asking for your help, it ain't so, but they do not need to know ;). So, I do not want to introduce fancy code to create nice borders, etc. I just want something that is visually appealing, clean and simple. If I do not, then my presentation will not achieve its goal.
Thank you, my code is below:
class App:
def __init__(self, parent):
frame = Frame(parent)
self.__setup_gui(frame) # Call Helper
frame.pack(padx=15, pady=15)
parent.title('To be changed')
def __setup_gui(self, frame):
# First Row
self.cs_label = Label(frame, text='Change Set: ')
self.cs_label.pack(side=LEFT, padx=10, pady=10)
self.cs_val = Entry(frame, width=10)
self.cs_val.pack(side=LEFT, padx=10, pady=10)
self.get_button = Button(frame, text='Get', command=self.get_content)
self.get_button.pack(side=LEFT, padx=10, pady=10)
# Text area and scrollbar
self.text_area = Text(frame, height=10, width=50, background='white')
# Put a scroll bar in the frame
scroll = Scrollbar(frame)
self.text_area.configure(yscrollcommand=scroll.set)
self.text_area.pack(side=TOP)
scroll.pack(side=RIGHT,fill=Y)
self.clipboard_var = IntVar()
self.notepad_var = IntVar()
def get_content(self):
print(self.clipboard_var.get())
print(self.notepad_var.get())
###################################################################################################
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
You definitely want the grid manager -- Pack only works for a vertical or horizontal stackup by itself. You can use multiple frames to work around it, but I find it's easier to expand a GUI if you just do it with Grid to start.
Here's what I've worked up real quick based what you said and the code. I reduced/removed the padding -- it looked huge for me -- and I set up two scrollbars, in a subframe to make the padding work out more easily. Note that to make the horizontal scrollbar useful your Text area needs to have wrap=NONE; otherwise you might as well use the easy 'ScrolledText' widget from tkinter.scrolledtext and skip the horizontal scroll bar.
I've now reframed things a bit to allow for resize, with a minimum size that shows the top buttons -- see the uses of minsize and row/columnconfigure.
BTW, it looks like your variables aren't being pulled from anywhere -- is that intentional?
from tkinter import *
class App:
def __init__(self, parent):
self.__setup_gui(parent) # Call Helper
parent.title('To be changed')
def __setup_gui(self, parent):
# First Row
self.rowframe = Frame(parent)
self.rowframe.grid()
self.cs_label = Label(self.rowframe, text='Change Set: ')
self.cs_label.grid(row=0, column=0, padx=2, pady=2)
self.cs_val = Entry(self.rowframe, width=10)
self.cs_val.grid(row=0, column=1, padx=2, pady=2)
self.get_button = Button(self.rowframe, text='Get', command=self.get_content)
self.get_button.grid(row=0, column=2, padx=2, pady=2)
parent.update_idletasks()
parent.minsize(width=self.rowframe.winfo_width(), height=self.rowframe.winfo_height())
# Text area and scrollbars
self.textframe = Frame(parent)
self.textframe.grid(row=1, columnspan=2, padx=2, pady=2, sticky=N+S+E+W)
self.hscroll = Scrollbar(self.textframe, orient=HORIZONTAL)
self.vscroll = Scrollbar(self.textframe)
self.text_area = Text(self.textframe, height=10, width=50, wrap=NONE, background='white', yscrollcommand=self.vscroll.set, xscrollcommand=self.hscroll.set)
self.text_area.grid(row=0, column=0, sticky=N+S+E+W)
self.hscroll.config(command=self.text_area.xview)
self.hscroll.grid(row=1, column=0, sticky=E+W)
self.vscroll.config(command=self.text_area.yview)
self.vscroll.grid(row=0, column=1, sticky=N+S)
# Row 0 defaults to 0
parent.rowconfigure(1, weight=1)
parent.columnconfigure(1, weight=1)
# Textarea setup
self.textframe.rowconfigure(0, weight=1)
self.textframe.columnconfigure(0, weight=1)
self.clipboard_var = IntVar()
self.notepad_var = IntVar()
def get_content(self):
print(self.clipboard_var.get())
print(self.notepad_var.get())
###################################################################################################
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
Now, all that said...you might get more visual appeal with PyGTK, PyQt, or wxPython, though tkinter coming "standard" is a nice feature.

Resources