changing colour of ttk.Progressbar elements in the xpnative theme - python - python

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')

Related

Is the function call to update a tkinter label after a button command was executed reason for unexpected behavior?

first of all let me start off by saying I started coding around 6 weeks ago in self-study, so it is very likely, that the approach may still be a total mess.
I have a program running (fine for me) in cli, but I want to make it usable for people that faint, when they see plain white text on a black background which they have to operate without a mouse.
Besides my main window, which is running in the background I wanted to have a message window, which displays the information if all necessary files where selected. which is shown below.
files_to_open = {'File_1':'', 'File_2':'', 'File_3':''}
def selectfiles_window():
global message_window
message_window = Tk()
...
content = Label(message_window, text=get_open_file_status_txt(), **txt_status_general)
content.pack(side='top')
button_select_file1 = Button(message_window,text = 'File 1',font = txt_general['font'],command = lambda:(
select_file_action(name='File 1', filetypename='Excel workbook', extension='*.xlsx'),
content.configure(text=get_open_file_status_txt())))
button_select_file1(side='bottom')
message_window.mainloop()
def select_file_action(name, filetypename, extension):
global files_to_open
files_to_open[name] = filedialog.askopenfilename(title = f'Select {name}', filetypes=[(filetypename, extension)])
def get_open_file_status_txt():
global files_to_open
message =f'''
[File_1] is {"NOT SET" if (files_to_open["File_1"] == "") else "SET"}'''
return message
I expected, that the text is updated after the filedialog was closed (which is partly working as expected).
Now what I don't understand: If I click the button to select File_1 and cancel it for the first time, the value for key File_1 is set to (). Any time after that, if I click the button to select File_1 and cancel it, the value for key File_1 is set to ''. If I select a file the path is saved correctly as value (also on the first attempt). If I cancel it is set again to ''.
Anybody an idea about why the value is set to () on the first cancel but afterwards runs as expected?
I would also be grateful for a different solution to update the text, if my approach is totally off.
Thank you and best regards,
Thomas
Turns out, that the function call was not the issue, rather that it is a (strange to me but maybe intended) behavior of filedialog.askopenfilename, which returns an empty tuple if cancel is selected on first call but an empty string on every further canceled calls.

how do I print a title for the top of my running program to continuously be visible for a code that prints and runs an endless amount of data?

Example: (to stay visible on the running program, able to view it at anytime if needed to scroll to the top)
print("this is my title")
print("here is my sub title")
count = 0
while count < 5000:
print("hello")
count = count + 1 # or count += 1
My code runs for as long as I set it too, that's not the problem. But when the program runs, it never shows the top printed title, or if I were to stop the program for a moment and physically scroll to the top, that printed title and other various text isn't visible.
How do I fix this to where, even if I wanted to print a million items, I could still see the printed title and printed information at the top?
First a useful non python way:
If you run a script (say my_long_print_script.py) from the terminal you can use less (in linux and osx for sure):
python my_long_print_script.py | less
then use enter to scroll down and q to quit.
Writing to stdout in python
In python you can directly write to stdout and 'overwrite' your previous output. This can lead to some sort of progress bar behaviour, I'm not sure this is what you want, here is an example:
# content of my_long_print_script.py:
import sys
from time import sleep
sys.stdout.write('This title should stay\n')
sys.stdout.write('this subtitle too\n')
for count in xrange(100):
sleep(0.1)
sys.stdout.write('{0}\r'.format(count))
sys.stdout.flush()
When you run this as a script (so you type python my_very_long_print_script.py in the terminal) the two title lines will persist and below a single line will continuously be updated.
FYI: I added the sleep because the count changes too quickly otherwise.
Hope this was somehow useful.
You'll probably want to use python's curses library. This is how you create "windows" that can be redrawn in-place.
I wrote a CLI version of 2048 that would scroll the terminal every time I output the game's board after a move. Using curses, I can now just overwrite the previous board without any scrolling.
Basically you'll want to initialize a new curses window object, set your output string to "My Title Here" and issue a window.redraw() command at (0,0) every time your program iterates.

Automatically scrolling down in PGU

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).

How to make forcus highlight for 2 objects at the same time

I want both view below are blue, how to set it? please help me! when i forcus to the second line i want it highlight both of object are blue, not one blue and one grey as below.
Code like this:
ui = twin_gtk_builder('twin.ui', ['dia_support', 'liststore7'])
win = ui.get_object('dia_support')
##### Begin function tree view
liststore = gtk.ListStore(int, int, int)
liststore.append([1,2,3])
liststore.append([2,2,2])
liststore.append([4,4,4])
win.sw = gtk.ScrolledWindow()
win.sm = gtk.TreeModelSort(liststore)
##### Set sort column
n = 1
win.sm.set_sort_column_id(n, gtk.SORT_ASCENDING)
win.tv = gtk.TreeView(win.sm)
win.vbox.pack_start(win.sw)
win.sw.add(win.tv)
win.tv.column = [None] * 3
win.tv.column[0] = gtk.TreeViewColumn('0-1000')
win.tv.column[1] = gtk.TreeViewColumn('0-1000000')
win.tv.column[2] = gtk.TreeViewColumn('-10000-10000')
win.tv.cell = [None] * 3
for i in range(3):
win.tv.cell[i] = gtk.CellRendererText()
win.tv.append_column(win.tv.column[i])
win.tv.column[i].set_sort_column_id(i)
win.tv.column[i].pack_start(win.tv.cell[i], True)
win.tv.column[i].set_attributes(win.tv.cell[i], text=i)
##### End function tree view
win.show_all()
and how it work
Tried one more time with #PM 2Ring help, Thanks so much for your help!
Somebody did it like this, but i can't find his contact...
I had to do a bit of work to get that code to run, Sunshine jp. In future, please try to post code that others can run & test, especially if it's GUI code. Otherwise it can be very hard to work out what the problem is and how to fix it.
I'm not familiar with twin_gtk1_builder(). Is it a GTK1 function?
Anyway, I've modified your code to run on GTK2+. I'm not quite sure what you want your code to do. So I've given row 2 a background color of cyan. Also, I've added the ability to make multiple selections, either using Ctrl or Shift on the keyboard when you select with the mouse; you can also do multiple selection with the keyboard with shift up and down arrows.
When the window loses focus the selected row(s) stays blue on my system. Maybe that's a feature of GTK2 that GTK1 doesn't have. (Or maybe it's due to my window manager - I'm using KDE 4.5.3 on Mepis Linux).
#!/usr/bin/env python
'''
TreeView test
From http://stackoverflow.com/questions/25840091/how-to-make-forcus-highlight-for-2-objects-at-the-same-time
'''
import pygtk
#pygtk.require('2.0')
import gtk
def TreeViewTest():
def delete_event(widget, event, data=None):
gtk.main_quit()
return False
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.set_title("TreeView Test")
win.set_size_request(320, 160)
win.connect("delete_event", delete_event)
win.vbox = gtk.VBox()
win.add(win.vbox)
win.sw = gtk.ScrolledWindow()
win.vbox.pack_start(win.sw)
##### Begin function tree view
#Set up liststore data. Column 3 controls whether
# background color of the TreeView is default or special.
liststore = gtk.ListStore(int, int, int, bool)
liststore.append([1, 2, 3, False])
#Tell row 2 to use the special color
liststore.append([2, 2, 2, True])
liststore.append([4, 4, 4, False])
win.sm = gtk.TreeModelSort(liststore)
##### Set initial sort column
n = 1
win.sm.set_sort_column_id(n, gtk.SORT_ASCENDING)
win.tv = gtk.TreeView(win.sm)
win.sw.add(win.tv)
win.tv.column = [None] * 3
win.tv.column[0] = gtk.TreeViewColumn('0-1000')
win.tv.column[1] = gtk.TreeViewColumn('0-1000000')
win.tv.column[2] = gtk.TreeViewColumn('-10000-10000')
#Set up cell renderers
win.tv.cell = [None] * 3
for i in range(3):
win.tv.cell[i] = gtk.CellRendererText()
win.tv.cell[i].set_property('cell-background', 'cyan')
win.tv.append_column(win.tv.column[i])
win.tv.column[i].set_sort_column_id(i)
win.tv.column[i].pack_start(win.tv.cell[i], True)
#win.tv.column[i].set_attributes(win.tv.cell[i], text=i)
win.tv.column[i].set_attributes(win.tv.cell[i], text=i,
cell_background_set=3)
#Allow multiple selection
treeselection = win.tv.get_selection()
treeselection.set_mode(gtk.SELECTION_MULTIPLE)
##### End function tree view
win.show_all()
def main():
TreeViewTest()
gtk.main()
if __name__ == "__main__":
main()
Note that this is NOT a good way to make a GUI. You should be creating a proper class, not adding everything as an attribute to win. Please see the PyGTK 2.0 Tutorial for plenty of code examples.
Edit
Ok. Sorry about my earlier confusion over what your problem is. At least we've now got a nice simple example of a PyGTK program that creates a TreeView. :)
Anyway, it turns out that I was right when I guessed that the blue color of the selection turning to grey when the window loses focus on your computer is due to the behaviour of the window manager. I suppose there may be a way to block that in the application, by playing with Widget attributes, but I'm not sure how to do that. And besides, it's considered rude for programs to ignore the settings in the users' window theme.
So the most appropriate solution to your problem is to make the appropriate change in your window manager's appearance settings.
In KDE the relevant property is called "Inactive selection changes color", as described in Color Scheme Options:
Inactive selection changes color — If checked, the current selection in elements which do not have input focus will be drawn using a different color. This can assist visual identification of the element with input focus in some applications, especially those which simultaneously display several lists.
To change this, open up system settings (ALT+F2 → "systemsettings", or the [K] menu → system settings), then go to "Application appearance" and select "Colors". In the "Options" tab, uncheck the "Inactive selection changes color" setting, and click apply.
... ... ...
If you're not using KDE you'll have to figure out for yourself how to change it; hopefully, other window manager settings interfaces and documentation refer to this property with the same name or a similar name.

Simple Hotkey Script in Python - How to set a global hotkey to send a string of text?

I was wondering how I could use wxPython along with the win32apis to create a simple script that will activate a window (if it is not already active) with a certain title and output text (keystrokes). One possible application for this would be keyboard shortcuts in gaming. I have read up on the wxPython RegisterHotKey(), but- as an amateur Python programmer- it is unclear to me.
The basic structure of the script will be:
Define the hotkey (something like win+F_)
Watch for the hotkey keystroke
See if the desired window (title) is already active, and activate it if it isn't
Simulate the typing of some text
I know there are simpler methods to accomplish this (such as AutoHotkey), but I feel more comfortable using something I have written myself and have taken an interest in Python.
Thanks!
For the record, I am using Python 2.7 on Windows 7 AMD64, though I doubt that the interpreter version/platform/architecture makes much of a difference here.
Are you talking about activating a window that you created in wx or a separate application, like notepad? If it's with wx, then it's trivial. You'd just use Raise() to bring whatever frame you need into focus. You would probably use PubSub or PostEvent to let the sub-frame know that it needs to Raise.
If you're talking about notepad, then things get much stickier. Here's an ugly hack I created based on some stuff I got from various locations on the web and the PyWin32 mailing list:
def windowEnumerationHandler(self, hwnd, resultList):
'''
This is a handler to be passed to win32gui.EnumWindows() to generate
a list of (window handle, window text) tuples.
'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
def bringToFront(self, windowText):
'''
Method to look for an open window that has a title that
matches the passed in text. If found, it will proceed to
attempt to make that window the Foreground Window.
'''
secondsPassed = 0
while secondsPassed <= 5:
# sleep one second to give the window time to appear
wx.Sleep(1)
print 'bringing to front'
topWindows = []
# pass in an empty list to be filled
# somehow this call returns the list with the same variable name
win32gui.EnumWindows(self.windowEnumerationHandler, topWindows)
print len(topWindows)
# loop through windows and find the one we want
for i in topWindows:
if windowText in i[1]:
print i[1]
win32gui.ShowWindow(i[0],5)
win32gui.SetForegroundWindow(i[0])
# loop for 5-10 seconds, then break or raise
handle = win32gui.GetForegroundWindow()
if windowText in win32gui.GetWindowText(handle):
break
else:
# increment counter and loop again
secondsPassed += 1
Then I used the SendKeys package to send text to the window (see http://www.rutherfurd.net/python/sendkeys/). If the user opens anything else, the script will break or weird things will happen. If you open something like MS Office, use win32com instead of SendKeys. That's much more reliable.

Categories