I am using PGU as a GUI for a game I am writing in python3 with pygame. This game is going to have multiplayer in it so I have been working on an in-game chat system. The chat system works and is displayed in a scroll area without problems however the scroll area does not scroll down automatically when new messages are received. This means every time there is a new message the user needs to scroll down manually to read the new messages. Does anyone know of a way to do this with PGU? Or does someone have recommendations for alternative way of doing this? Looking around on my own I found this example indicating it can be done however the code posted there doesn't seem to show the part I am looking for. Here is a dumbed down version of my own code. When a chat message is received then chatmessage is called automatically.
class ChatScreen:
def __init__(self):
self.desktop = gui.Desktop(theme=gui.Theme("data/themes/default/"))
self.container = gui.Container(width=800,height=600)
self.chatinput = gui.Input(size=65)
self.chatdoc = gui.Document(width=1, height=10)
self.chatscroll = gui.ScrollArea(self.chatdoc,width=600,height=100,hscrollbar=False)
self.container.add(self.chatinput, 10, 550)
self.container.add(self.chatscroll, 10, 440)
self.desktop.init(self.container)
def chatmessage(self, message):
self.chatdoc.add(gui.Label(message))
self.chatdoc.br(1)
I found my own solution to this problem. I'm posting the answer for anyone else that might have this issue in the future. Here is my updated example code with the fix:
class ChatScreen:
def __init__(self):
self.desktop = gui.Desktop(theme=gui.Theme("data/themes/default/"))
self.container = gui.Container(width=800,height=600)
self.chatinput = gui.Input(size=65)
self.chatdoc = gui.Document(width=1, height=10)
self.chatscroll = gui.ScrollArea(self.chatdoc,width=600,height=100,hscrollbar=False)
self.container.add(self.chatinput, 10, 550)
self.container.add(self.chatscroll, 10, 440)
self.desktop.init(self.container)
def chatmessage(self, message):
self.chatdoc.add(gui.Label(message))
self.chatdoc.br(1)
self.desktop.loop()
self.chatscroll.set_vertical_scroll(someint)
I found that set_vertical_scroll() forcibly sets where the scroll area is. By setting someint to a number greater then what the number of messages in the chat box it goes to the bottom. In a real working solution someint will need to be a variable that increases with the number of messages or there has to be a limit to the number of messages that are displayed (which is what I have done with my project).
Related
I'm working on a chat program in Python and would like to add some user friendly interface to my client. The fact is that I gave myself the challenge
of using only terminal.
So I found the urwid module to work with, which is cross-platform and well-documented online.
After reading the manual and watching the tutorial of the module I didn't really know how to write this interface but I acquired some knowledge about the theory (Widgets, different types of object, how is the screen partitioned...)
So I ended up finding some pieces of code on stackoverflow or github, I found a listBox example which will really help me for the logs-keeping part of the screen.
Now I need to create a permanent input area on the bottom to take input from the user. I did not find any code or discussion on how to do this. How can I create a permanent input area at the bottom to accept input from the user?
Any links or examples of code will be appreciated! :)
Thanks everybody,
Elliot
The tutorial has several self contained examples that demonstrate some basic features.
For a simple approach, I might suggest using a Frame object, with focus_part set to 'footer'. A basic example that moves the prompt text to the main window:
import urwid
text_str = 'Here are a few previous lines.of text that populate.the main terminal window.Press "return" to add the prompt line to the main window.or press escape to exit.'.replace('.', '\n')
def main():
my_term = MyTerminal()
urwid.MainLoop(my_term).run()
class MyTerminal(urwid.WidgetWrap):
def __init__(self):
self.screen_text = urwid.Text(text_str)
self.prompt_text = urwid.Edit('prompt: ', '')
self._w = urwid.Frame(header=urwid.Pile([urwid.Text('header text'),
urwid.Divider()]),
body=urwid.ListBox([self.screen_text]),
footer=self.prompt_text,
focus_part='footer')
def keypress(self, size, key):
if key is 'esc':
raise urwid.ExitMainLoop()
if key == 'enter':
self.screen_text.set_text(self.screen_text.text +
'\n' +
self.prompt_text.edit_text)
self.prompt_text.edit_text = ''
return
super(MyTerminal, self).keypress(size, key)
main()
I'm using python 2.7 and TK to make a gui which accesses text files and uses data in them to do many things, but the one relevant here is sending a gchat message.
Currently, I have everything working, the point I need some help with is when I call my module to send the message, the message is sent perfectly, although I wanted the user to have an indication of the process happening, so I created a ttk.progressbar. but there is a few things I'd like to improve on this:
1) I would like to change the appearance of the actual bar, upon viewing the source files, I couldn't see any options, and when I googled the problem the only fix I could find was to change the source code, I'm pretty sure this would only change it when ran with my files, then when the user runs it, it would be the standard? preferably, I'd like the bar to be transparent, although blue would work, I've seen some people having blue as a state in window machines, windows is my main concern, so if I could get say, blue in windows, but native elsewhere, that would be fine.
2)this one is hopefully a bit more simple, but when the button is pressed it takes values from user input which can still be changed, maybe altering the outcome of the function, is there anyway to stop all input to a tk window, then resume when the function is complete?
below is what I have so far, thank you for the help
self.progressbar = ttk.Progressbar(self.gcTableButtonsFrame, length = 70, orient=HORIZONTAL, mode ='determinate')
self.progressbar.grid(column = 0, row = 0, sticky = 'n s')
#we then pass through the extension and the string 'test' through this fnction from the gchat module which will then send a
#gchat message to the extension passed through
self.bytes = 0
self.maxbytes = 0
self.start()
self.t = thread.start_new_thread(gchat.sendGChatMessage,(text, "test"))
except IndexError:
tkMessageBox.showinfo("Invalid Entry", "Please first select an Entry to send to")
def start(self):
self.progressbar["value"] = 0
self.maxbytes = 50000
self.progressbar["maximum"] = 50000
self.read_bytes()
def read_bytes(self):
'''simulate reading 500 bytes; update progress bar'''
selection2 = self.gcTable.selection()
self.bytes += 700
self.progressbar["value"] = self.bytes
if self.bytes < self.maxbytes:
# read more bytes after 100 ms
Tk.after(self.mainPyWindow, 100, self.read_bytes)
else:
tkMessageBox.showinfo("Message Sent", "A GChat message has been sent to " + self.gcTable.item(selection2, 'values')[1])
self.progressbar.destroy()
Your first question is somewhat ambiguous, in that I'm not sure whether you are talking about controlling the look of the progress bar or the nature of the progress it is displaying.
The nature of the progress bar is controlled via its value and its mode option (determinate progress bars are different from indeterminate ones; the former are controlled via the value, the latter just show that “something is happening”).
The look of a progress bar is controlled by the overall theme. On Windows and Mac OS X, the default theme is the system theme meaning that the progress bar will look native, whatever that is. I've not experimented recently with themes on Linux so I forget what they look like there; switch themes with:
# Switch to the included 'clam' theme
s = ttk.Style()
s.theme_use('clam')
OK, so iv almost completed my program for my project but I cant get a BUTTON_EVT to work which if i am honest should be the easiest thing todo. I have the buttons on my program which represent the hardware and I have created a def function for them to appear on the OGL canvas.
Problem has been solved... The code associated with the problem is found in the answer below
Edited from your last comment. Use this (using your own images):
def OnClickRouter(self, event):
image=wx.Image('cat.jpg', wx.BITMAP_TYPE_JPEG)
self.frame = bucky(None, image)
self.frame.Show()
If you call bucky() this way you must also fix the class signature:
class bucky(wx.Frame):
# Creating the outer window/frame
def __init__(self, parent, image=None):
wx.Frame.__init__(self, parent, -1,'Karls Network Tool', size=(900,700))
my_image = image if image else wx.Image("myself.bmp", wx.BITMAP_TYPE_BMP)
''''''''''''''''''''''''''''''''
# Button images
buttonOneRouter = my_image.ConvertToBitmap()
self.buttonOneRouter = wx.BitmapButton(panel, -1, buttonOneRouter, pos=(20,340))
self.buttonOneRouter.Bind(wx.EVT_BUTTON, self.OnClickRouter)
''''''''''''''''''''''''''''''''
Then you can see that after clicking the buttonOnerouter what actually you are doing is opening a new frame. The left figure is what I get when I run the program, the right one is after I click and enter again my name (I simplified a bit your code. Thats why you only see one button at the bottom instead of 4):
If you want to put my cat in the canvas instead of in the button there is still some work to do. I recommend to you to give a look at the wxPython demo. In the miscellaneous group of examples you have one called OGL that shows how to do that.
Edit: You can download the wxPython docs and demos package from here
I don't know if this is right or not but I suggest you to take this approach and see if it works or not.
Modify your frame class as:
def __init(self,parent,id,img=None)
def onClickRouter(self,event):
image=wx.Image('router.jpg', wx.BITMAP_TYPE_JPEG)
temp = image.ConvertToBitmap()
self.bmp = wx.StaticBitmap(parent=self, bitmap=temp)
self.frame=bucky(self.bmp)
Please let know the outcome.
I am currently making a chat application in python. I am having 2 separate codes: one for the server and one for the client. The server script is taking the login data of new clients that connects and in another thread manages the messages that he has to receive and send.
The client application is made into a class and works well, excepts that when the script requests the UI to show, the only new window is a empty one:
def __init__(self, master):
self.nr=0
self.frameul=self.tbox=self.txt=self.scrollbar=self.button=self.roottk=[0]*20
self.OameniSiIduri={}
self.LoginUI(master)
self.framestate=""
def ChatUI(self, peer_id):
no=self.no
self.no+=1
self.PeoplesAndId[peer_id]=no
self.base[no]=Toplevel()
self.theframe[no] = Frame(self.base[no])
self.theframe[no].pack()
self.entry[no] = Entry(self.theframe[no], width=95)
self.tbox[no] = Text(self.theframe[no], state=DISABLED, wrap=WORD)
self.button[no] = Button(self.theframe[no], text="Send", fg="green", command=lambda x=self.entry[no].get(), y=peer_id, z=self.tbox[nr]: self.Sendmsg(x,y,z), width=10)
self.tbox[no].pack(side=TOP, fill=X)
self.button[no].pack(side=RIGHT)
self.entry[no].pack(side=LEFT)
.....
All the vars and the functions are declared. Can anyone give me a hint about what can the cause of this problem is?
Found myself the mistake after searching all the night through the code. Apparently if i use this line:
self.theframe=self.tbox=self.entry=self.scrollbar=self.button=self.base=[0]*20
All the objects point to the same value.
My guess is, the code that is creating the UI is throwing an error that you are not seeing. For example, are you importing DISABLED and WORD properly? If not, the code would fail after creating the frame but before creating the other widgets, leaving you with an empty widget.
One way to debug this is to give each toplevel and frame a distinct color. This lets you see which are visible and which are not -- maybe you're looking at a window or frame and thinking it's one thing when it's something else.
Alright, first off I'm not quite sure how to phrase my problem. This could be lack of sleep, or being pretty new to Python and GTK, or a combination. To aid me, I have written a complete bare-bones example with the help of zetcode.com's tutorials.
The problem, as well as I can put it, is a menu item - with no sub-menus - takes two clicks to activate. Unlike a sub-menu item activating on a single click. This is mildly annoying (and likely to confuse future users), but not really causing any problems with my application. I would, however, like to resolve it.
My actual application is being created with the help of Ubuntu Quickly - but the problem exists while using gtkBuilder or straight-gtk.
Here is the bare-bones example:
#!/usr/bin/python
import gtk
class MenuTest(gtk.Window):
def __init__(self):
super(MenuTest, self).__init__()
self.set_title("Menus, how do they work?!")
self.set_size_request(350, 200)
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
self.set_position(gtk.WIN_POS_CENTER)
mb = gtk.MenuBar()
filemenu = gtk.Menu()
filem = gtk.MenuItem("Some Action")
filem.connect("activate", self.on_file_activate)
mb.append(filem)
vbox = gtk.VBox(False, 2)
vbox.pack_start(mb, False, False, 0)
self.add(vbox)
self.connect("destroy", gtk.main_quit)
self.show_all()
def on_file_activate(self, widget):
md = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, "herp derp, took two clicks to show me")
md.run()
md.destroy()
MenuTest()
gtk.main()
Hopefully someone can help, and not completely confuse this noob at the same time.
You can solve your problem by connecting to the 'button-press-event' signal instead of the 'activate' signal, and making your callback like this:
def on_file_activate(self, widget, event):
if event.button != 1:
return False #only intercept left mouse button
md = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, "herp derp, I only needed one click")
md.run()
md.destroy()
return True
However, why would you want to do that? I'm not surprised that your original code didn't work as expected, because that's not really what menus are for. You'd be better off using a toolbar button, or a regular button. I think misusing a menu as a button is more likely to confuse future users.
I know this is a fairly old thread. But, for the sake of anyone else trying to accomplish this task, the simplest solution is to replace the "activate" signal with the "select" signal. That should fix it. At least, it does on my box.
ie. replace
filem.connect("activate", self.on_file_activate)
with
filem.connect("select", self.on_file_activate)
I would also change the function name for the sake of clarity.
I hope that helps someone. =)