I am developing a demo program based on PyGobject that has parts in its interface like stocks management that should refresh every few seconds. The program has the need to be able to run 24/7 while keeping the displayed information correct.
I have a container which has its content in stacks(and separately there are tabs to change between them):
def main_content(self):
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.stack = Gtk.Stack()
self.stack.get_style_context().add_class("main-container")
self.stack.props.margin = 20
self.child1_child = self.child1()
self.child1_child.set_visible(True)
self.stack.add_named(self.child1_child, "Child1")
self.child2_child = self.child2()
self.child2_child.set_visible(True)
self.stack.add_named(self.child2_child, "Child2")
self.child3_child = self.child2()
self.child3_child.set_visible(True)
self.stack.add_named(self.child3_child, "Child3")
box.pack_start(self.stack, True, True, 0)
if self.redraw:
print("Redrawing")
self.redraw = True
return box
So, there are functions to generate each stack member, they are assigned to variables, and then set as visible and added to the stack.
Lets suppose that the stack Child1 has a list of stocks, and i don't want it to have more than 5 minutes of delay. I tried to do a redraw function where a list is making the tabs, and this redraw function is either evoked by a timeout_add or a tab click :
def redraw(self):
selected_stack = self.listbox.get_selected_row().get_index()
# **Possible** solution:
stack_elements = {0: "Child1", 1: "Child2", 2: "Child3"}
variables = {0: "child1_child", 1: "child2_child", 2: "stock_child"}
self.variables[janela].destroy()
self.variables[janela] = self.stack_elements[janela].lower()
self.variables[janela].set_visible(True)
self.stack.add_named(self.variables[janela], stack_elements[janela])
# Problems: Doesn't works as python code. Just an idea
# **Possible** solution 2:
self.stack.destroy()
self.stack = Gtk.Stack()
self.stack_content = self.main_content()
self.main_box.pack_start(self.stack_content, True, True, 0)
These were just some try-error ideas, i never made an interface with the need to be kept updated, is there any proper way to do so, or a way to make one of my ways work?
Related
I'm trying to create a GUI, in the nav menu you can click a cascade option to open another window where you can click roll to generate a set of numbers. It comes up with error. I think it's because the function is called from another function I just don't know how to get that function to call it/ if there is any other ways to fix this. I've tried global functions and looking it up but haven't found anything other than using classes so far, which I don't know how to do.
line 147, in totalRolls
txtresultsOut.set(totalRollResults)
NameError: name 'txtresultsOut' is not defined
Here is the code that is relevant to it. I've called the function to skip having to input all the other code for the main gui window.
def rollSix():
s = 0
numbers = [0,0,0,0]
for i in range(1,5):
numbers[s] = randrange(1,7)
s += 1
numbers.remove(min(numbers))
Result = sum(numbers)
totalRollResults.append(Result)
def totalRolls():
rollOne()
rollTwo()
rollThree()
rollFour()
rollFive()
rollSix()
txtresultsOut.set(totalRollResults)
def rollw():
rollWindow = tix.Tk()
rollWindow.title("Dice Rolls")
diceLabel = Label(rollWindow, text = "Click Roll for your Stats")
diceLabel.grid(row = 0, column = 0)
rollBtn = Button(rollWindow, text = "Roll Stats", command = totalRolls)
rollBtn.grid(row = 1, column = 0)
txtresultsOut = StringVar()
resultsOut = Entry(rollWindow, state = "readonly", textvariable = txtresultsOut)
resultsOut.grid(row = 2, column = 0)
rollw()
first of all I would NOT recommend using StringVar(). You can use the .get() method of Entry to obtain the value inside the same. Try this way and make a global declaration of the Entry whose values you want to get in other functions.
EDIT------------
#you can use the following code to make your entry active to be edited.
entry.configure(state='normal')
# insert new values after deleting old ones (down below)
entry.delete(0,END)
entry.insert(0, text_should_be_here)
# and finally make its state readonly to not let the user mess with the entry
entry.configure(state='readonly')
Im writing screen scraping application in python, using transitions to handle the state machine.
The initial state is looking for a GUI window. When the window has been found, the state machine changes to next state.
Please consider the following code:
class ScreenScrapper(object):
window = None
def is_window_found(self):
return bool(self.window)
def state_look_for_window(self):
window = get_window() # Returns a bitmap object or None if window is not found
self.do_work()
def state_do_work(self):
print('Do some work!')
print('Window er: ', self.window)
states = ['dummy', 'state_look_for_window', 'state_do_work']
transitions = [
{'trigger': 'start', 'source': 'dummy', 'dest': 'state_look_for_window', 'after': 'state_look_for_window'},
{'trigger': 'do_work', 'source': 'state_look_for_window', 'dest': 'state_do_work', 'conditions': 'is_window_found', 'after': 'state_do_work'},
]
screen_scrapper = ScreenScrapper()
Machine(model=screen_scrapper, states=states, transitions=transitions, initial='dummy')
screen_scrapper.start()
In this simple example, the start changes states from dummy to state_look_for_window. The after callback will look for the window and afterwards change state to state_do_work. This transition has the condition is_window_found
Question: How can state_look_for_window be executed again as long as the transition condition is_window_found return False? Please note: I'm only interested in a solution that can be contained within the state machine. In other words, the only code outside must remain screen_scrapper.start().
Since you just need to transition from one state to another you can just do a state transition after checking is_window_found
I think it should look like this
def state_look_for_window(self):
if not is_window_found:
self.state_look_for_window()
else:
window = get_window() # Returns a bitmap object or None if window is not found
self.do_work()
I think you are asking for a 'reflexive transition', which is a trigger having the same State as source and destination.
I would replace your current diagram by another State called 'Window Ready', and specify an internal transition for that state, where you remain cycling inside the same, until the desired Window GUI is found.
I am very new to PyQt, so I am not even sure where to start searching for this.
So I have two different options for QRadioButtons which ideally will correspond to two QPushButtons, one each.
Basically, I have the following code, where i tried to achieve this by using if statements:
def tab1UI(self):
mytabfont = QFont('Lucida Sans Unicode', 9)
layout = QFormLayout()
#self.setTabText(0,"My Data")
self.tab1.setLayout(layout)
tabdescription = 'To obtain or generate data choose an option below:'
# radio options
label1 = QLabel(tabdescription)
label1.setFont(mytabfont)
layout.addWidget(label1)
radiobtn1 = QRadioButton('Load data from file')
radiobtn1.setChecked(True)
#why does my resize not work?
radiobtn1.resize(100,100)
radiobtn1.setFont(mytabfont)
layout.addWidget(radiobtn1)
loadbtn = QPushButton('Open CSV file...')
layout.addWidget(loadbtn)
radiobtn2 = QRadioButton('Generate data')
radiobtn2.setFont(mytabfont)
genbtn= QPushButton('Generating matrix...')
layout.addWidget(radiobtn2)
layout.addWidget(genbtn)
if radiobtn1.isChecked():
# if this option is clicked then this button needs to be activated else it must be de-activated
loadbtn.setEnabled(True)
genbtn.setEnabled(False)
elif radiobtn2.isChecked():
loadbtn.setEnabled(False)
genbtn.setEnabled(True)
else:
loadbtn.setEnabled(False)
genbtn.setEnabled(False)
So, whenever I click one radio-button option I would like one pushbutton to become automatically active or inactive when the other option is checked instead.
There must be some sort of Action to be connected but not sure how to go about this.
You're only running the if statement once, when the buttons are first created. In order for this to work, you need to evaluate those if statements every time the radio button check state is changed. Qt allows you to do this with Signals and Slots. The QRadioButton will emit a signal when you change the check state. You can connect to this signal and run a function that updates the enabled state of the other buttons.
def tab1UI(self):
mytabfont = QFont('Lucida Sans Unicode', 9)
layout = QFormLayout()
self.tab1.setLayout(layout)
tabdescription = 'To obtain or generate data choose an option below:'
# radio options
self.label1 = QLabel(tabdescription)
self.label1.setFont(mytabfont)
layout.addWidget(self.label1)
self.radiobtn1 = QRadioButton('Load data from file')
self.radiobtn1.setChecked(True)
self.radiobtn1.setFont(mytabfont)
layout.addWidget(self.radiobtn1)
self.loadbtn = QPushButton('Open CSV file...')
layout.addWidget(self.loadbtn)
self.radiobtn2 = QRadioButton('Generate data')
self.radiobtn2.setFont(mytabfont)
self.genbtn= QPushButton('Generating matrix...')
layout.addWidget(self.radiobtn2)
layout.addWidget(self.genbtn)
self.radiobtn1.toggled.connect(self.refresh_button_state)
self.radiobtn2.toggled.connect(self.refresh_button_state)
self.refresh_button_state()
def refresh_button_state(self):
if self.radiobtn1.isChecked():
self.loadbtn.setEnabled(True)
self.genbtn.setEnabled(False)
elif self.radiobtn2.isChecked():
self.loadbtn.setEnabled(False)
self.genbtn.setEnabled(True)
else:
self.loadbtn.setEnabled(False)
self.genbtn.setEnabled(False)
I have the following Python MFC code. I have a listbox which I fill with some values, and when the user clicks on the values I want the static text control to be updated with the current selection. There are two problems with this code. The first is that the value in the text control is only updated the first time I click on the listbox. The second is that the value lags behind the real selected value in the listbox, presumably because the control handles the click after my handler code gets called. I'd appreciate help with either of these issues.
An odd thing, (perhaps a clue) is that when I mouse-down over the 'OK' button but move away for mouse-up the static text does get updated as I would expect.
I've tried RedrawWindow(), UpdateWindow(), ShowWindow() on both the control and the dialog, and nothing seems to make any difference.
import win32con
from pywin.mfc import dialog
IDC_LIST = 9000
IDC_TEXT = 9001
class ChooserDialog(dialog.Dialog):
def __init__(self):
DIALOGTEMPLATE = [
["Test", (0, 0, 254, 199), win32con.WS_CAPTION | win32con.DS_MODALFRAME, None, (8, "MS SansSerif")],
[128, "OK", win32con.IDOK, (197,178,50,14), win32con.BS_PUSHBUTTON | win32con.WS_VISIBLE],
["listbox", "List", IDC_LIST, (7,7,177,186), win32con.WS_VISIBLE],
["static", "", IDC_TEXT, (197,7,50,160), win32con.WS_CHILD | win32con.WS_VISIBLE]
]
dialog.Dialog.__init__(self, DIALOGTEMPLATE)
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
for i in ["one", "two", "three"]:
self.GetDlgItem(IDC_LIST).AddString(i)
self.HookCommand(self.OnNotify, IDC_LIST)
return rc
def OnNotify(self, ctrl, action):
if ctrl == IDC_LIST:
selected = self.GetDlgItem(IDC_LIST).GetCurSel()
self.SetDlgItemText(IDC_TEXT, "%d" % selected)
self.GetDlgItem(IDC_TEXT).RedrawWindow()
return 1
dia = ChooserDialog()
dia.DoModal()
The first problem with the code was that the win32con.LBS_NOTIFY style wasn't set for the listbox control. If that isn't set you won't get any messages from the LB. The few messages that I was getting were due to other events in the dialog.
The second problem was that I was using HookCommand(), which intercepts commands, and allows you to handle them. Instead I wanted HookMessage() to get only the notifications. It seems notifications get called after the update of the control, so that's exactly what I wanted.
The LBN_SELCHANGE notification, according to MSDN documentation is received through the WM_COMMAND message. It seems I must subscribe to all WM_COMMAND messages in the dialog, and then filter them in my handler. The LBN_SELCHANGE documentation explains about what is passed, and in conjunction with the HookMessage() Python documentation you can work out how to handle it.
Here is the working code:
import win32con
from pywin.mfc import dialog
IDC_LIST = 9500
IDC_TEXT = 9501
class ChooserDialog(dialog.Dialog):
def __init__(self):
DIALOGTEMPLATE = [
["Test", (0, 0, 254, 199), win32con.WS_CAPTION | win32con.DS_MODALFRAME, None, (8, "MS SansSerif")],
[128, "OK", win32con.IDOK, (197,178,50,14), win32con.BS_PUSHBUTTON | win32con.WS_VISIBLE],
["listbox", "List", IDC_LIST, (7,7,177,186), win32con.WS_VISIBLE|win32con.LBS_NOTIFY],
["static", "", IDC_TEXT, (197,7,50,160), win32con.WS_CHILD | win32con.WS_VISIBLE]
]
dialog.Dialog.__init__(self, DIALOGTEMPLATE)
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
for i in ["one", "two", "three"]:
self.GetDlgItem(IDC_LIST).AddString(i)
self.HookMessage(self.OnNotifyCommand, win32con.WM_COMMAND)
return rc
def OnNotifyCommand(self, data):
msg_id = data[1] # should always be WM_COMMAND
wParam = data[2]
lParam = data[3]
list_id = wParam & 0xffff
notification_code = (wParam & 0xffff0000) >> 16
if list_id != IDC_LIST: return # not our list box.
if notification_code != win32con.LBN_SELCHANGE: return # Not a change of selection
selected = self.GetDlgItem(IDC_LIST).GetCurSel()
self.SetDlgItemText(IDC_TEXT, "%d"%selected)
dia = ChooserDialog()
dia.DoModal()
Well, while I don't consider myself an experienced programmer, especially when it comes to multimedia applications, I had to do this image viewer sort of a program that displays images fullscreen, and the images change when the users press <- or -> arrows.
The general idea was to make a listbox where all the images contained in a certain folder (imgs) are shown, and when someone does double click or hits RETURN a new, fullscreen frame is generated containing, at first, the image selected in the listbox but after the user hits the arrows the images change in conecutive order, just like in a regular image viewer.
At the beginning, when the first 15 - 20 images are generated, everything goes well, after that, although the program still works an intermediary, very unpleasant and highly unwanted, effect appears between image generations, where basically some images that got generated previously are displayed quickly on the screen and after this the right, consecutive image appears. On the first apparitions the effect is barelly noticeble, but after a while it takes longer and longer between generations.
Here's the code that runs when someone does double click on a listbox entry:
def lbclick(self, eve):
frm = wx.Frame(None, -1, '')
frm.ShowFullScreen(True)
self.sel = self.lstb.GetSelection() # getting the selection from the listbox
def pressk(eve):
keys = eve.GetKeyCode()
if keys == wx.WXK_LEFT:
self.sel = self.sel - 1
if self.sel < 0:
self.sel = len(self.chk) - 1
imgs() # invocking the function made for displaying fullscreen images when left arrow key is pressed
elif keys == wx.WXK_RIGHT:
self.sel = self.sel + 1
if self.sel > len(self.chk) - 1:
self.sel = 0
imgs() # doing the same for the right arrow
elif keys == wx.WXK_ESCAPE:
frm.Destroy()
eve.Skip()
frm.Bind(wx.EVT_CHAR_HOOK, pressk)
def imgs(): # building the function
imgsl = self.chk[self.sel]
itm = wx.Image(str('imgs/{0}'.format(imgsl)), wx.BITMAP_TYPE_JPEG).ConvertToBitmap() # obtaining the name of the image stored in the list self.chk
mar = itm.Size # Because not all images are landscaped I had to figure a method to rescale them after height dimension, which is common to all images
frsz = frm.GetSize()
marx = float(mar[0])
mary = float(mar[1])
val = frsz[1] / mary
vsize = int(mary * val)
hsize = int(marx * val)
panl = wx.Panel(frm, -1, size = (hsize, vsize), pos = (frsz[0] / 2 - hsize / 2, 0)) # making a panel container
panl.SetBackgroundColour('#000000')
imag = wx.Image(str('imgs/{0}'.format(imgsl)), wx.BITMAP_TYPE_JPEG).Scale(hsize, vsize, quality = wx.IMAGE_QUALITY_NORMAL).ConvertToBitmap()
def destr(eve): # unprofessionaly trying to destroy the panel container when a new image is generated hopeing the unvanted effect, with previous generated images will disappear. But it doesn't.
keycd = eve.GetKeyCode()
if keycd == wx.WXK_LEFT or keycd == wx.WXK_RIGHT:
try:
panl.Destroy()
except:
pass
eve.Skip()
panl.Bind(wx.EVT_CHAR_HOOK, destr) # the end of my futile tries
if vsize > hsize: # if the image is portrait instead of landscaped I have to put a black image as a container, otherwise in the background the previous image will remain, even if I use Refresh() on the container (the black bitmap named fundal)
intermed = wx.Image('./res/null.jpg', wx.BITMAP_TYPE_JPEG).Scale(frsz[0], frsz[1]).ConvertToBitmap()
fundal = wx.StaticBitmap(frm, 101, intermed)
stimag = wx.StaticBitmap(fundal, -1, imag, size = (hsize, vsize), pos = (frsz[0] / 2 - hsize / 2, 0))
fundal.Refresh()
stimag.SetToolTip(wx.ToolTip('Esc = exits fullscreen\n<- -> arrows = quick navigation'))
def destr(eve): # the same lame attempt to destroy the container in the portarit images situation
keycd = eve.GetKeyCode()
if keycd == wx.WXK_LEFT or keycd == wx.WXK_RIGHT:
try:
fundal.Destroy()
except:
pass
eve.Skip()
frm.Bind(wx.EVT_CHAR_HOOK, destr)
else: # the case when the images are landscape
stimag = wx.StaticBitmap(panl, -1, imag)
stimag.Refresh()
stimag.SetToolTip(wx.ToolTip('Esc = exits fullscreen\n<- -> arrows = quick navigation'))
imgs() # invocking the function imgs for the situation when someone does double click
frm.Center()
frm.Show(True)
Thanks for any advice in advance.
Added later:
The catch is I'm trying to do an autorun presentation for a DVD with lots of images on it. Anyway it's not necessarely to make the above piece of code work properly if there are any other options. I've already tried to use windows image viewer, but strangely enough it doesn't recognizes relative paths and when I do this
path = os.getcwd() # getting the path of the current working directory
sel = listbox.GetSelection() # geting the value of the current selection from the list box
imgname = memlist[sel] # retrieving the name of the images stored in a list, using the listbox selection, that uses as choices the same list
os.popen(str('rundll32.exe C:\WINDOWS\System32\shimgvw.dll,ImageView_Fullscreen {0}/imgsdir/{1}'.format(path, imgname))) # oepning the images up with bloody windows image viewer
it opens the images only when my program is on the hard disk, if it's on a CD / image drive it doesn't do anything.
There's an image viewer included with the wxPython demo package. I also wrote a really simple one here. Either one should help you in your journey. I suspect that you're not reusing your panels/frames and instead you're seeing old ones that were never properly destroyed.