Repository: qtile/qtile

Issue:
No filename provided Image widget causes QTile to crash.
# The issue
When no filename argument, OR an invalid filename is provided for the Image widget, Qtile seems to crash, and needs to be killed to restart. You are obviously not supposed to provide a non-existant image, but I have doubts that it crashing is intended behavior. 

What I am describing here as a "crash" is no keyboard input being accepted, and windows from *all* other workspaces being displayed on the workspace you are currently on. If this is not actually a crash, I apologize, but regardless, Qtile becomes unusable until the process is killed and I am kicked back to my Display Manager.

# Steps to reproduce
In your bar, create a new ``Image`` widget somewhere inside. Either provide a path to an image that does not exist, or do not provide one period.

# Qtile version
This is the commit hash of the version I am running.
6c4d0557124989d46ffb2bb24f4468db687fcdb2

# Stack traces
No stack traces from xsession-errors, or the Qtile log are produced, however I have traced the error (through using the logger provided in the module's file) to the ``_configure`` method of the Image widget, and it seems to be coming the line: ``base._Widget._configure(self, qtile, bar)``

# Configuration
https://pastebin.com/qxBq6yPn

If there is any information I got wrong here, or some other bit of information I can provide that will help this issue get solved, I will try my best.


Discussion:
Looks like we probably moved inital drawing earlier to base.Widget's _configure somehow. I really don't think we should be drawing there at all, at least on the first _configure, since we don't know what to draw yet.

I think the bug is that we now call _reset_surface() in Drawer's __init__, and we probably shouldn't. @DorianGray thoughts?
reset surface doesn't draw, it just reinitializes the in memory recording surface used to stage drawing operations. If something is drawing early it' outside of the drawer changes.
Looking at the source for the image widget, in both of those scenarios (no filename or non-existent file) the widget will raise a `ValueError` in `_configure`. Those errors aren't caught by `bar._configure` which I'm guessing causes qtile to crash.

Do we just need better error handling? I'll take a look.

Resolving patch:
diff --git a/libqtile/widget/image.py b/libqtile/widget/image.py
--- a/libqtile/widget/image.py
+++ b/libqtile/widget/image.py
@@ -53,14 +53,17 @@ def __init__(self, length=bar.CALCULATED, width=None, **config):
 
     def _configure(self, qtile, bar):
         base._Widget._configure(self, qtile, bar)
+        self.img = None
 
         if not self.filename:
-            raise ValueError("Filename not set!")
+            logger.warning("Image filename not set!")
+            return
 
         self.filename = os.path.expanduser(self.filename)
 
         if not os.path.exists(self.filename):
-            raise ValueError("File does not exist: {}".format(self.filename))
+            logger.warning("Image does not exist: {}".format(self.filename))
+            return
 
         img = Img.from_path(self.filename)
         self.img = img
@@ -75,6 +78,9 @@ def _configure(self, qtile, bar):
             img.resize(width=new_width)
 
     def draw(self):
+        if self.img is None:
+            return
+
         self.drawer.clear(self.background or self.bar.background)
         self.drawer.ctx.save()
         self.drawer.ctx.translate(self.margin_x, self.margin_y)
@@ -88,6 +94,9 @@ def draw(self):
             self.drawer.draw(offsety=self.offset, height=self.width)
 
     def calculate_length(self):
+        if self.img is None:
+            return 0
+
         if self.bar.horizontal:
             return self.img.width + (self.margin_x * 2)
         else:


Test patch: