I am working on an Sonos Controller using the PyQT lib.
When i change to netxt or prev track, the labels are updated with the artist, title and album info. They are cut-off tho. Anyone experienced this before ? Its not a length issue, as the cutting off seems to change.
This is the code I update the labels with:
def currentTrackInfo(self):
currentTrackInfoDict = {}
currentZone = str(self.cb.currentText())
deviceDict = self.sonosZonesCoordinatorsIP()
for key, value in deviceDict.items():
if value == currentZone:
device = SoCo(key)
track = device.get_current_track_info()
current_title = track['title']
current_artist = track["artist"]
current_album = track["album"]
currentTrackInfoDict.update({"current_title": current_title})
currentTrackInfoDict.update({"current_artist": current_artist})
currentTrackInfoDict.update({"current_album": current_album})
self.artImage()
return currentTrackInfoDict
def updateTrackInfo(self):
self.artistlabel.clear
self.albumlabel.clear
self.titlelabel.clear
self.currentTrackInfoDict = self.currentTrackInfo()
self.artistlabel.setText("{}".format(self.currentTrackInfoDict["current_artist"]))
self.albumlabel.setText("{}".format(self.currentTrackInfoDict["current_album"]))
self.titlelabel.setText("{}".format(self.currentTrackInfoDict["current_title"]))
Related
I have an urwid layout like so:
The code for this layout is like so:
import urwid
class DashboardUI:
orders_txt: urwid.widget.Text
def show_or_exit(self, key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
self.orders_txt.set_text(repr(key))
def show(self):
algo_view = urwid.Text(u"Main Content Goes Here")
algo_view_fill = urwid.Filler(algo_view, 'top')
algo_view_linebox = urwid.LineBox(algo_view_fill,
tlcorner=u'', tline=u'', lline=u'', trcorner=u'',
blcorner=u'', rline=u'│', bline=u'', brcorner=u'│')
self.orders_txt = urwid.Text(u"Orders\norder2\norder3")
orders_fill = urwid.Filler(self.orders_txt, 'bottom')
orders_linebox = urwid.LineBox(orders_fill,
tlcorner=u'', tline=u'', lline=u'', trcorner=u'',
blcorner=u'─', rline=u'', bline=u'─', brcorner=u'─')
counter_txt = urwid.Text(u"Counter 1\nCounter 2\nCounter 3")
counters_fill = urwid.Filler(counter_txt, 'top')
counters_linebox = urwid.LineBox(counters_fill,
tlcorner=u'', tline=u'', lline=u'', trcorner=u'',
blcorner=u'─', rline=u'', bline=u'─', brcorner=u'─')
resources_txt = urwid.Text(u"Resources 1\nResources 2\nResources 3")
resources_fill = urwid.Filler(resources_txt, 'top')
resources_linebox = urwid.LineBox(resources_fill,
tlcorner=u'', tline=u'', lline=u'', trcorner=u'',
blcorner=u'', rline=u'', bline=u'', brcorner=u'')
right_pile = urwid.Pile([orders_linebox, counters_linebox, resources_linebox])
layout = urwid.Columns([algo_view_linebox, right_pile], dividechars=0)
loop = urwid.MainLoop(layout, unhandled_input=self.show_or_exit)
loop.run()
I would like the left column to take 75% of the available space, and the right one to take 25% (ideally with a maximum 100 chars in width).
How can I make it so that it has this altered behavior?
Found it.
Have to add columns while also specifying their weight, like so:
layout = urwid.Columns([('weight', 3, algo_view_linebox), right_pile])
For the right-side widget, no need to specify the weight as the default is 1.
Code:
fields = ['Label1','Label2','Label3','Label4','Label5','Label6']
selected_item = ['Entry1','Entry2','Entry3','Entry4','Entry5','Label6']
stringvar_list = [tk.StringVar() for s in range(len(selected_item))]
for var in range(len(stringvar_list)):
stringvar_list[var].set(selected_item[var])
for i in range(len(fields)):
lbl = ttk.Label(root,text=fields[i])
ent = ttk.Entry(root,textvariable=stringvar_list[i])
lbl.grid(row=row,column=col,padx=2,pady=2)
ent.grid(row=row,column=col+1,padx=2,pady=2)
row+=1
When I run this code, I get this(the entries are not filled):
But when I run the same code in debugging mode,no changes - I just skip through the breakpoint, I get this(Entries are filled):
I'm using VS Code, if it matters.
Extended Code:
def modify(fields):
# fields = fields[1:] #Original
fields = ['Label1','Label2','Label3','Label4','Label5','Label6'] # For Simplicity and Underestanding
# selected_item = tree.item(tree.selection()[0])['values'][1:] #Original
selected_item = ['Entry1','Entry2','Entry3','Entry4','Entry5','Label6'] # For Simplicity and Underestanding
modify_top = tk.Toplevel(root)
stringvar_list = [tk.StringVar() for s in range(len(selected_item))]
for var in range(len(stringvar_list)):
stringvar_list[var].set(selected_item[var])
row = 0
col = 0
frm = ttk.Frame(modify_top)
for i in range(len(fields)):
lbl = ttk.Label(frm,text=fields[i])
ent = ttk.Entry(frm,textvariable=stringvar_list[i])
lbl.grid(row=row,column=col,padx=2,pady=2)
ent.grid(row=row,column=col+1,padx=2,pady=2)
row+=1
frm.grid(sticky='nsew',row=0,column=0,padx=30,pady=20)
make_changes_btn = ttk.Button(modify_top,text='Make Changes',command = lambda : make_changes())
make_changes_btn.grid(row=1,column=0)
Issues:
As #acw1668 commented, making stringvar_list global did the job, but I'm referencing it's values in the same function, so that shouldn't be a problem.
Even if the issue is with the scope, then why does the program work fine in debugging mode?
How do you turn off (or hide) the seconds y axis scale on a combination chart in openpyxl?
I can find the xml difference by comparing the before and after changes to hide the scale (I just change the excel file extension to '.zip' to access the xml):
-<c:valAx>
<c:axId val="156672520"/>
-<c:scaling>
<c:orientation val="minMax"/>
</c:scaling>
<c:delete val="0"/>
<c:axPos val="r"/>
<c:majorGridlines/>
<c:numFmt sourceLinked="1" formatCode="0.0"/>
<c:majorTickMark val="out"/>
<c:minorTickMark val="none"/>
<c:tickLblPos val="none"/> # this changes from 'nextTo'
<c:crossAx val="207247000"/>
<c:crosses val="max"/>
<c:crossBetween val="between"/>
</c:valAx>
I've tried this (last few lines are the 'tickLblPos' ):
mainchart = LineChart()
mainchart.style = 12
v2 = Reference(WorkSheetOne, min_col=1, min_row=2+CombBarLineDataOffsetFromTop, max_row=3+CombBarLineDataOffsetFromTop, max_col=13)
mainchart.add_data(v2, titles_from_data=True, from_rows=True)
mainchart.layout = Layout(
ManualLayout(
x=0.12, y=0.25, # position from the top
h=0.9, w=0.75, # this is scaling the chart into the container
xMode="edge",
yMode="edge",
)
)
mainchart.title = "Chart Title"
# Style the lines
s1 = mainchart.series[0]
#Marker type
s1.marker.symbol = "diamond" # triangle
s1.marker.size = 9
s1.marker.graphicalProperties.solidFill = "C00000" # Marker filling
s1.marker.graphicalProperties.line.solidFill = "000000" # Marker outline
s1.graphicalProperties.line.noFill = False
# Line color
s1.graphicalProperties.line.solidFill = "000000" # line color
s2 = mainchart.series[1]
s2.graphicalProperties.line.solidFill = "000000"
s2.graphicalProperties.line.dashStyle = "dash"
mainchart.dataLabels = DataLabelList()
mainchart.dataLabels.showVal = False
mainchart.dataLabels.dLblPos = 't'
mainchart.height = 15
mainchart.width = 39
#Create the Chart
chart2 = BarChart()
chart2.type = "col"
chart2.style = 10 # simple bar
chart2.y_axis.axId = 0
dataone = Reference(WorkSheetOne, min_col=2, min_row=CombBarLineDataOffsetFromTop+1, max_row=CombBarLineDataOffsetFromTop+1, max_col=13 )
doneseries = Series(dataone, title="Series Title")
chart2.append(doneseries)
cats = Reference(WorkSheetOne, min_col=2, min_row=CombBarLineDataOffsetFromTop, max_row=CombBarLineDataOffsetFromTop, max_col=13)
chart2.set_categories(cats)
# Set the series for the chart data
series3Total = chart2.series[0]
fill3Total = PatternFillProperties(prst="pct5")
fill3Total.foreground = ColorChoice(srgbClr='996633') # brown
fill3Total.background = ColorChoice(srgbClr='996633')
series3Total.graphicalProperties.pattFill = fill3Total
chart2.dataLabels = DataLabelList()
chart2.dataLabels.showVal = False
chart2.shape = 2
mainchart.y_axis.crosses = "max"
mainchart.y_axis.tickLblPos = "none" # nextTo -- this doesn't work
mainchart += chart2
WorkSheetOne.add_chart(mainchart, 'A1')
How can I translate the difference in the XML to an attribute with openpyxl?
The problem here is with some of default values for some attributes which use 3-valued logic at times so that None != "none", ie. <c:tickLblPos /> != <c:tickLblPos val="none"/> because the default is "nextTo". This plays havoc with the Python semantics (3-valued logic is always wrong) where the default is not to set an attribute if the value is None in Python. This really only affects ChartML and I've added some logic to the descriptors for the relevant objects so that "none" will be written where required.
But this code isn't publicly available yet. Get in touch with my by e-mail if you'd like a preview.
I am trying to write a small bit of code that interactively deletes selected slices in an image series using matplotlib. I have created a button 'delete' which stores a number of indices to be deleted when the button 'update' is selected. However, I am currently unable to reset the range of my slider widget, i.e. removing the number of deleted slices from valmax. What is the pythonic solution to this problem?
Here is my code:
import dicom
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
frame = 0
#store indices of slices to be deleted
delete_list = []
def main():
data = np.random.rand(16,256,256)
nframes = data.shape[0]
raw_dicom_stack = []
for x in range (nframes):
raw_dicom_stack.append(data[x,:,:])
#yframe = 0
# Visualize it
viewer = VolumeViewer(raw_dicom_stack, nframes)
viewer.show()
class VolumeViewer(object):
def __init__(self, raw_dicom_stack, nframes):
global delete_list
self.raw_dicom_stack = raw_dicom_stack
self.nframes = nframes
self.delete_list = delete_list
# Setup the axes.
self.fig, self.ax = plt.subplots()
self.slider_ax = self.fig.add_axes([0.2, 0.03, 0.65, 0.03])
self.delete_ax = self.fig.add_axes([0.85,0.84,0.1,0.04])
self.update_ax = self.fig.add_axes([0.85,0.78,0.1,0.04])
self.register_ax = self.fig.add_axes([0.85,0.72,0.1,0.04])
self.add_ax = self.fig.add_axes([0.85,0.66,0.1,0.04])
# Make the slider
self.slider = Slider(self.slider_ax, 'Frame', 1, self.nframes,
valinit=1, valfmt='%1d/{}'.format(self.nframes))
self.slider.on_changed(self.update)
#Make the buttons
self.del_button = Button(self.delete_ax, 'Delete')
self.del_button.on_clicked(self.delete)
self.upd_button = Button(self.update_ax, 'Update')
self.upd_button.on_clicked(self.img_update)
self.reg_button = Button(self.register_ax, 'Register')
self.add_button = Button(self.add_ax, "Add")
# Plot the first slice of the image
self.im = self.ax.imshow(np.array(raw_dicom_stack[0]))
def update(self, value):
global frame
frame = int(np.round(value - 1))
# Update the image data
dat = np.array(self.raw_dicom_stack[frame])
self.im.set_data(dat)
# Reset the image scaling bounds (this may not be necessary for you)
self.im.set_clim([dat.min(), dat.max()])
# Redraw the plot
self.fig.canvas.draw()
def delete(self,event):
global frame
global delete_list
delete_list.append(frame)
print 'Frame %s has been added to list of slices to be deleted' %str(frame+1)
print 'Please click update to delete these slices and show updated image series \n'
#Remove duplicates from delete list
def img_update(self,event):
#function deletes image stacks and updates viewer
global delete_list
#Remove duplicates from list and sort into numerical order
delete_list = list(set(delete_list))
delete_list.sort()
#Make sure delete_list is not empty
if not delete_list:
print "Delete list is empty, no slices to delete"
#Loop through delete list in reverse numerical order and remove slices from series
else:
for i in reversed(delete_list):
self.raw_dicom_stack.pop(i)
print 'Slice %i removed from dicom series \n' %(i+1)
#Can now remove contents from delete_list
del delete_list[:]
#Update slider range
self.nframes = len(self.raw_dicom_stack)
def show(self):
plt.show()
if __name__ == '__main__':
main()
In order to update a slider range you may set the min and max value of it directly,
slider.valmin = 3
slider.valmax = 7
In order to reflect this change in the slider axes you need to set the limits of the axes,
slider.ax.set_xlim(slider.valmin,slider.valmax)
A complete example, where typing in any digit changes the valmin of the slider to that value.
import matplotlib.pyplot as plt
import matplotlib.widgets
fig, (ax,sliderax) = plt.subplots(nrows=2,gridspec_kw=dict(height_ratios=[1,.05]))
ax.plot(range(11))
ax.set_xlim(5,None)
ax.set_title("Type number to set minimum slider value")
def update_range(val):
ax.set_xlim(val,None)
def update_slider(evt):
print(evt.key)
try:
val = int(evt.key)
slider.valmin = val
slider.ax.set_xlim(slider.valmin,None)
if val > slider.val:
slider.val=val
update_range(val)
fig.canvas.draw_idle()
except:
pass
slider=matplotlib.widgets.Slider(sliderax,"xlim",0,10,5)
slider.on_changed(update_range)
fig.canvas.mpl_connect('key_press_event', update_slider)
plt.show()
It looks like the slider does not have a way to update the range (api). I would suggest setting the range of the slider to be [0,1] and doing
frame = int(self.nframes * value)
On a somewhat related note, I would have made frame an instance variable a data attribute instead of a global variable (tutorial).
How would I add another event to the pane created in our GUI manager class (below). I want to detect when the x button closes the pane. I've tried using the same format as wx.EVT_MENU for wx.EVT_CLOSE but it didn't work.
def popup_panel(self, p):
"""
Add a panel object to the AUI manager
:param p: panel object to add to the AUI manager
:return: ID of the event associated with the new panel [int]
"""
ID = wx.NewId()
self.panels[str(ID)] = p
self.graph_num += 1
if p.window_caption.split()[0] in NOT_SO_GRAPH_LIST:
windowcaption = p.window_caption
else:
windowcaption = 'Graph'#p.window_caption
windowname = p.window_name
# Append nummber
captions = self._get_plotpanel_captions()
while (1):
caption = windowcaption + '%s'% str(self.graph_num)
if caption not in captions:
break
self.graph_num += 1
# protection from forever-loop: max num = 1000
if self.graph_num > 1000:
break
if p.window_caption.split()[0] not in NOT_SO_GRAPH_LIST:
p.window_caption = caption
#p.window_caption = windowcaption+ str(self.graph_num)
p.window_name = windowname + str(self.graph_num)
style1 = self.__gui_style & GUIFRAME.FIXED_PANEL
style2 = self.__gui_style & GUIFRAME.FLOATING_PANEL
if style1 == GUIFRAME.FIXED_PANEL:
self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
Name(p.window_name).
Caption(p.window_caption).
Position(10).
Floatable().
Right().
Dock().
MinimizeButton().
Resizable(True).
# Use a large best size to make sure the AUI
# manager takes all the available space
BestSize(wx.Size(PLOPANEL_WIDTH,
PLOPANEL_HEIGTH)))
self._popup_fixed_panel(p)
elif style2 == GUIFRAME.FLOATING_PANEL:
self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
Name(p.window_name).Caption(p.window_caption).
MinimizeButton().
Resizable(True).
# Use a large best size to make sure the AUI
# manager takes all the available space
BestSize(wx.Size(PLOPANEL_WIDTH,
PLOPANEL_HEIGTH)))
self._popup_floating_panel(p)
# Register for showing/hiding the panel
wx.EVT_MENU(self, ID, self.on_view)
if p not in self.plot_panels.values() and p.group_id != None:
self.plot_panels[ID] = p
if len(self.plot_panels) == 1:
self.panel_on_focus = p
self.set_panel_on_focus(None)
if self._data_panel is not None and \
self._plotting_plugin is not None:
ind = self._data_panel.cb_plotpanel.FindString('None')
if ind != wx.NOT_FOUND:
self._data_panel.cb_plotpanel.Delete(ind)
if caption not in self._data_panel.cb_plotpanel.GetItems():
self._data_panel.cb_plotpanel.Append(str(caption), p)
return ID
I want to be able to pick up the event in the plotting child class.
def create_panel_helper(self, new_panel, data, group_id, title=None):
"""
"""
## Set group ID if available
## Assign data properties to the new create panel
new_panel.set_manager(self)
new_panel.group_id = group_id
if group_id not in data.list_group_id:
data.list_group_id.append(group_id)
if title is None:
title = data.title
new_panel.window_caption = title
new_panel.window_name = data.title
event_id = self.parent.popup_panel(new_panel)
#remove the default item in the menu
if len(self.plot_panels) == 0:
pos = self.menu.FindItem(DEFAULT_MENU_ITEM_LABEL)
if pos != -1:
self.menu.Delete(DEFAULT_MENU_ITEM_ID)
# Set UID to allow us to reference the panel later
new_panel.uid = event_id
# Ship the plottable to its panel
wx.CallAfter(new_panel.plot_data, data)
self.plot_panels[new_panel.group_id] = new_panel
# Set Graph menu and help string
helpString = 'Show/Hide Graph: '
for plot in new_panel.plots.itervalues():
helpString += (' ' + plot.label + ';')
self.menu.AppendCheckItem(event_id, new_panel.window_caption,
helpString)
self.menu.Check(event_id, IS_WIN)
wx.EVT_MENU(self.parent, event_id, self._on_check_menu)
wx.EVT_CLOSE(self.parent, event_id, self._on_close_panel)
wx.EVT_SHOW(new_panel, self._on_show_panel)
Did you try catching wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE or wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSED? I would think that would do what you want. I am assuming you're using wx.aui rather than wx.agw.aui. I suspect the latter is similar though.
EDIT: Oops. I read this wrong. I thought the OP wanted to know about AUINotebook. The event you're probably looking for is wx.aui.EVT_AUI_PANE_CLOSE