pygtk Child Label resizes Fixed container while moving - label

I'm writing a "flying text" with pygtk.
A small test code looks like this:
class MainWindow(gobject.GObject):
def __init__(self,sender):
self.__gobject_init__()
sender.connect('move_label', self.move_label)
self.box = HBox()
self.w = gtk.Window()
self._mainbox = gtk.VBox()
self._flybox = gtk.Fixed()
self._label = gtk.Label("testing")
self._x = 10
self._flybox.put(self._label,self._x,0);
self._mainbox.pack_start(self.box)
self._mainbox.pack_start(self._flybox)
self.w.add(self._mainbox)
def move_label(self,sender):
self._x += 10
self._flybox.move(self._label,self._x,0)
def main(self, fname):
self.w.show_all()
self.w.connect("destroy", gtk.main_quit)
gtk.main()
class Sender(gobject.GObject):
def __init__(self):
self.__gobject_init__()
def trigger_move_label(self):
gobject.timeout_add(2*1000, self.trigger_move_label)
self.emit('move_label');
gobject.signal_new('move_label',Sender,gobject.SIGNAL_RUN_FIRST,gobject.TYPE_NONE,())
sender = Sender()
gobject.timeout_add(2*1000, sender.trigger_move_label)
player = VideoPlayer(sender)
player.main(sys.argv[1])
This example creates a window with a Fixed box at the bottom containing a label "testing".
Also it creates a timer that triggers every 2 seconds a method that moves (self._flybox.move(........)) label to the end of the container.
The problem is that after label is moved to the end of the window. It keeps moving and resizes the Fixed container and whole window.
But I want this label to be croped while it moves out of the Fixed container

I've figred it out.
I need to use gtk.Layout() instead of gtk.Fixed() to make label crop after moving out of the container

Related

Close all windows except for the starting window in tkinter

In this code below, I made a simple route guidance by opening defined windows each time I press a specific button e.g.) Mainmenu -> Classroom Floor -> Classroom No.
I'm trying to make a 'Home' button that will close all open windows except for the first Main Menu window with WELCOME written.
For example, after I press btn7, btn702, then I have 4 windows open. I would like to add a 'Home' button that will close 3 newly open windows and leave the first window alive. How might I make this kind of button?
from os import system
from tkinter import *
from PIL import ImageTk, Image
mainmenu = Tk()
mainmenu.title("CAU 310 GUIDE MAP")
mainmenu.geometry("1280x800+0+0")
canvas = Canvas(mainmenu, width = 1280, height = 800)
canvas.pack(fill='both', expand = True)
canvas.create_text(640, 250, text = 'WELCOME', font=times 45)
btnclassroom = Button(mainmenu, padx=5, pady=5,text="Classroom", font="times 30", command=selectfloor)
btnclassroom.place(x=140, y=570)
def selectfloor():
mainmenu = Tk()
mainmenu.title("DESTINATION")
mainmenu.geometry("1280x800+0+0")
mainmenu.config(bg='white')
canvas = Canvas(mainmenu, width = 1280, height = 800)
canvas.pack(fill='both', expand = True)
canvas.create_text(640, 150, text = 'Select floor of classroom', font='Arial 40')
btn7=Button(mainmenu, padx=4, pady=4, text="7F", font="Arial 42 bold", command=floor7)
btn7.place(x=160-5, y=490)
def floor7():
mainmenu = Tk()
mainmenu.title("FLOOR 7")
mainmenu.geometry("1280x800+0+0")
canvas = Canvas(mainmenu, width = 1280, height = 800)
canvas.pack(fill='both', expand = True)
canvas.create_text(640, 150, text = 'Select classroom No.', font='Arial 40')
btn702=Button(mainmenu, padx=3, pady=3, text="No.702", font="Arial 38 bold", command=room702)
btn702.place(x=100+220*1, y=240)
mainmenu.mainloop()
def room702():
mainmenu = Tk()
mainmenu.title("Elevator 1")
mainmenu.geometry("1280x800+0+0")
lobby = ImageTk.PhotoImage(Image.open("1F_elevator1.jpg"), master=mainmenu)
canvas = Canvas(mainmenu, width = 1280, height = 800)
canvas.pack(fill='both', expand = True)
canvas.create_image(0, 0, image=lobby,anchor = "nw")
mainmenu.mainloop()
mainmenu.mainloop()
**I've been googling around for this kind of matter, and I think I've got a hint of making the 'selectfloor', 'floor7', 'room702' window as a children widget. But I'm still not sure how to make it happen.
You can store the windows in a list, and then iterate over the list to destroy them.
Here's a simplified example. This example uses Toplevel windows rather than instances of Tk. It's not clear why you're using multiple instances of Tk, but in general you should only ever have one. This same technique works with any sort of widgets, however.
import tkinter as tk
windows = []
def delete_all_but_first():
for window in windows[1:]:
window.destroy()
def new_window():
window = tk.Toplevel(root)
windows.append(window)
window.title(f"Window #{len(windows)}")
root = tk.Tk()
windows.append(root)
del_all = tk.Button(root, text="Delete all", command=delete_all_but_first)
new = tk.Button(root, text="New window", command=new_window)
new.pack(side="top", padx=20, pady=20)
del_all.pack(side="top", padx=20, pady=20)
root.mainloop()
There's an optimization you can make if you use Toplevel instead of Tk, and if you always make the windows direct children of the root window. In that case you can just iterate over all children of root, and delete any window that is a top-level.
In this case, you don't need to maintain the windows list.
def delete_toplevels():
for child in root.winfo_children():
if child.winfo_class() == "Toplevel":
child.destroy()
def new_window():
window = tk.Toplevel(root)
window.title(f"Window #{len(windows)}")
...
del_all = tk.Button(root, text="Delete all", command=delete_toplevels)

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

Function to set image of buttons causes image to jump between buttons

I'm trying to teach myself the tkinter module by programming minesweeper. I have created a grid with buttons and a method to set an image flag to cells. It works, in that when you press the right mouse button the image of the button changes as desired, but when you right click on the next button the image just moves to the next button, rather than creating a second flag. I want to be able to place a new flag image on each cell that I right click, rather than just shuffle the image around. Here's my code:
import tkinter as Tk
def main():
root = Tk.Tk()
root.geometry('{}x{}'.format(700, 700))
instance = Minesweeper(root, 10, 10)
root.mainloop()
class Minesweeper:
def __init__(self, parent, height, width):
self.top_frame = Tk.Frame(parent)
self.top_frame.place(anchor=Tk.CENTER, relx=0.5, rely=0.5)
self.frames = []
self.buttons = []
index = 0
for x in range(height):
for y in range(width):
self.frames.append(Tk.Frame(self.top_frame, height=50, width=50))
self.buttons.append(Tk.Button(self.frames[index], bg="white"))
self.frames[index].grid_propagate(False)
self.frames[index].columnconfigure(0, weight=1)
self.frames[index].rowconfigure(0, weight=1)
self.frames[index].grid(row=x, column=y)
self.buttons[index].grid(sticky="wens")
self.buttons[index].bind('<Button-3>', self.flag)
index += 1
def flag(self, event):
self.flag = Tk.PhotoImage(file="flag.png")
event.widget.configure(image=self.flag)
if __name__ == "__main__":
main()
Seems that the below fixed it:
def flag(self, event):
self.flag = Tk.PhotoImage(file="flag.png")
event.widget.image = self.flag # <---- this seemed to fix it
event.widget.configure(image=self.flag)

How to handle sprite animation in Kivy

I am coding a game using Kivy. I have a Screen class where I put my animation code. It's not a usual game, it's more like several screens, each with its own animation, with button commands for going back and forth to different screens.
It works ok, but when I make more classes like this and put it all in a ScreenManager, the animation is disrupted with random white screens.
class Pas(Screen):
def __init__(self, **kwargs):
super(Pas, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1 / 60.0)
self.ani_speed_init = 15
self.ani_speed = self.ani_speed_init
self.ani = glob.glob("img/pas_ani*.png")
self.ani.sort()
self.ani_pos = 0
self.ani_max = len(self.ani)-1
self.img = self.ani[0]
self.update(1)
back = Button(
background_normal=('img/back-icon.png'),
background_down=('img/back-icon.png'),
pos=(380, 420))
self.add_widget(back)
def callback(instance):
sm.current = 'game'
back.bind(on_press=callback)
def update(self, dt):
self.ani_speed -= 1
if self.ani_speed == 0:
self.img = self.ani[self.ani_pos]
self.ani_speed = self.ani_speed_init
if self.ani_pos == self.ani_max:
self.ani_pos = 0
else:
self.ani_pos += 1
with self.canvas:
image = Image(source=self.img, pos=(0, 0), size=(320, 480))
What am I doing wrong? I am also accepting ideas for a different way of doing this.
If you want to use Screen and ScreenManager for your screens, it would be better to use the transition system they define and use, so, to define your own Transitions, and apply them. If you want more control, i would advise getting ride of Screen and ScreenManager, and just using Widgets, to control the whole drawing/positioning process.
Also, Clock.schedule_interval(self.update, 0) is equivalent to the call you are making, the animation will be called each frame, and you can use dt to manage the animation progress.
Also, kivy can manage gifs, as well as zip archives of images to directly do animations (useful to have animated pngs), you can let kivy manage the whole animation process this way.

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